Ponteiros [RESOLVIDO]

1. Ponteiros [RESOLVIDO]

Marco Brainiac
mbrainiac

(usa Debian)

Enviado em 09/09/2015 - 21:18h

Pessoal,

Estava seguindo esta vídeo aula e aos 11 min ele achou um número da memória e substituiu.
https://www.youtube.com/watch?v=zZlIy3hp0c0

Quando fiz no Gcc:

int main(){
int x = 10;
double y = 20.50;
char z = 'a';


int *px; /* OU int *px = &x */
px = &x;

double *py = &y;

char *pz = &z;

int *result; //11min5seg
result = &x;
// achando valor de memória subst aqui result = &x ;

printf("valor x= %d \n",result);

return 0;
}

achei
mbrainiac@hotpc:~/Área de trabalho/C_programas$ ./pont9.x
Endereço x= -1077077828


Ao substituir em:
int main(){
int x = 10;
double y = 20.50;
char z = 'a';


int *px; /* OU int *px = &x */
px = &x;

double *py = &y;

char *pz = &z;

int *result; //11min5seg
result = -1077077828; <======= substituir aqui par achar o valor conteúdo de x
// achando valor de memória subst aqui result = &x ;




printf("valor x= %i \n",*result);




printf("\n");

return 0;
}


Tenho o seguinte erro:

mbrainiac@hotpc:~/Área de trabalho/C_programas$ gcc pont9.c -o pont9.x
pont9.c: In function ‘main’:
pont9.c:21:10: warning: assignment makes pointer from integer without a cast
result = -1077077828;
^
mbrainiac@hotpc:~/Área de trabalho/C_programas$


A memória dá um número negativo e não consigo seguir os passos da vídeo aula






  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2015 - 23:49h

Eu não assisti à aula toda, ainda. Mas já assisti a outras aulas desse mesmo autor, e minha opinião geral sobre o conjunto da obra é a seguinte: é uma pena que ele seja tão bom comunicador, porque ele fala muita besteira, mas fala com uma naturalidade quase encantadora, que chega a ser perigosa para novatos e incautos.

Sem ter visto o vídeo, mas lendo os programas que você postou, não posso deixar de observar que há problemas. Mesmo o primeiro programa não compila limpo no GCC se você compilar com opções de diagnóstico ligadas. Aqui, ele mostrou, entre outros problemas (de variáveis declaradas mas não usadas, que não vêm ao caso), o seguinte erro:

prog1.c: In function ‘main’:
prog1.c:20:1: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("valor x= %d \n",result);
^


Em princípio, é um erro em C usar um ponteiro como se fosse um inteiro, ou vice-versa. Isso até pode ser feito, mas a boa prática recomenda deixar isso explícito no código, e de uma forma que seja segura (mais detalhes abaixo).

Uma das razões pelas quais o uso cruzado é um erro é que os tamanhos da palavras pode ser diferente. Por exemplo, numa máquina com arquitetura AMD64 (todos os nossos PCs de 64 bits), ponteiros têm 64 bits, mas inteiros têm apenas 32 (se você quiser trabalhar com inteiros de 64 bits, têm de usar explicitamente long). O contrário também seria possível (i.e. uma arquitetura em que ponteiros têm menos bits do que o tamanho do maior inteiro possível).

Isso, de cara, já torna o printf() acima perigoso, pois um valor de 64 bits vai ser exibido como se fosse apenas de 32, o que significa que alguns bits serão simplesmente suprimidos no valor impresso.

Outra razão pela qual o uso cruzado é perigoso é que algumas arquiteturas não aceitam qualquer valor inteiro como ponteiro, principalmente por questões de alinhamento. Por exemplo, uma máquina que só consegue endereçar a memória em blocos de 4 bytes (não é necessariamente o caso dos nossos PCs, mas tais arquiteturas existem) só podem ter ponteiros cujo valor, convertido em inteiro, é necessariamente um múltiplo de 4. Se você atribuir um valor inteiro que não é múltiplo de 4 a um ponteiro e tentar usar tal ponteiro para obter um dado, o processador provavelmente vai gerar uma exceção de erro de barramento.

Outro risco envolvido com o uso cruzado é que a aritmética de ponteiros garante um funcionamento coerente quando você usa o ponteiro para apontar para uma sequência de elementos (arrays, vetores, matrizes etc.). Se você usar um inteiro para apontar para um array, será responsabilidade sua saber o tamanho de cada elemento caso queira percorrer os elementos desse array e, além disso, terá de usar uma sintaxe mais complicada.

Fora os problemas com ponteiros em si, a ideia de substituir o valor de uma execução em execuções posteriores comete o erro grosseiro de assumir que determinada variável vai sempre ocupar a mesma posição na memória. Isso pode até ser verdade num determinado sistema ou ambiente de execução, mas não é uma verdade absoluta (colocar isso numa aula é um absurdo, que mostra que o cara não tem a menor ideia do que está realmente falando, embora fale com grande eloquência). Veja a saída de seis execuções consecutivas do primeiro programa na minha máquina, deixando claro que a variável muda de lugar.

$ ./a.out 
valor x= -994010612
$ ./a.out
valor x= -1212030180
$ ./a.out
valor x= -1080773204
$ ./a.out
valor x= 339011036
$ ./a.out
valor x= 1587733628
$ ./a.out
valor x= 1999101548


-----------------------

Intercambiar inteiros e ponteiros tem seu valor e é necessário em alguns poucos casos.

Nos casos em que é necessário, existem algumas ajudas dadas pelo padrão do C.

A primeira dessas ajudas é a definição de um tipo auxiliar de inteiro que garante uma quantidade de bits necessária para armazenar seguramente um ponteiro. Tal se chama intptr_t, e é definido em <stdint.h> (disponível a partir do padrão ISO de 1999 para a linguagem C).

Também há uma forma segura de fazer conversões. Essa forma é indicar todas as conversões explicitamente no código por meio dos operadores de coerção de tipos (type cast).

int i, *pi;
intptr_t i_pi;
double *pd;

i=1234567890;
pi=&i;

i_pi=(intptr_t)pi; /* Explicito conversão de ponteiro para inteiro. */

pd=(double *)i_pi; /* Explicito conversão de inteiro para ponteiro. */
/*
No caso, essa conversão provavelmente vai me causar
problemas quando eu tentar acesso a *pd, pois normalmente
sizeof(double)>sizeof(int).
*/


Outro exemplo.

/*
Código em DOS para acesso direto à memória de vídeo (não
vai funcionar no Linux ou no Windows, que protegem o sistema
de tentativas de acesso direto e arbitrário à memória).
*/
struct screen_character {
char character;
uint8_t attribute;
};

struct screen_character (*screen)[80]=
(struct screen_character (*)[80])0xb8000
; /* Ou 0xb8000000, dependendo da implementação. */

/*
Agora ‘screen’ pode ser usado como uma matriz com linhas de 80
carcateres de largura.
*/

/* Põe no canto superior esquerdo a letra P azul piscante com fundo vermelho. */
screen[0][0].attribute=ATTR_BLINK|ATTR_BLUE_FG|ATTR_RED_BG;
screen[0][0].character='P';

/* Na 10ª linha, 30ª coluna, põe um A branco. */
screen[9][29].attribute=ATTR_WHITE_FG;
screen[9][29].character='A';


Para imprimir o valor de um ponteiro, printf() tem uma conversão exclusiva, que é "%p". Se você quiser MUITO exibir o endereço como inteiro, o certo seria converter primeiro para intmax_t (também em <stdint.h>) e imprimir com a conversão "%jd" ou "%ju".

3. Re: Ponteiros [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/09/2015 - 00:57h

mbrainiac escreveu:

Pessoal,

Estava seguindo esta vídeo aula e aos 11 min ele achou um número da memória e substituiu.
https://www.youtube.com/watch?v=zZlIy3hp0c0


Depois de assistir, posso garantir que é praticamente um dever cívico negativar esse vídeo. Mais uma vez, ele fala um monte de besteiras, do início ao fim.


4. Re: Ponteiros [RESOLVIDO]

Marco Brainiac
mbrainiac

(usa Debian)

Enviado em 10/09/2015 - 16:29h

Obrigado, voltar a estudar pelo Dammas

paulo1205 escreveu:

Eu não assisti à aula toda, ainda. Mas já assisti a outras aulas desse mesmo autor, e minha opinião geral sobre o conjunto da obra é a seguinte: é uma pena que ele seja tão bom comunicador, porque ele fala muita besteira, mas fala com uma naturalidade quase encantadora, que chega a ser perigosa para novatos e incautos.

Sem ter visto o vídeo, mas lendo os programas que você postou, não posso deixar de observar que há problemas. Mesmo o primeiro programa não compila limpo no GCC se você compilar com opções de diagnóstico ligadas. Aqui, ele mostrou, entre outros problemas (de variáveis declaradas mas não usadas, que não vêm ao caso), o seguinte erro:

prog1.c: In function ‘main’:
prog1.c:20:1: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("valor x= %d \n",result);
^


Em princípio, é um erro em C usar um ponteiro como se fosse um inteiro, ou vice-versa. Isso até pode ser feito, mas a boa prática recomenda deixar isso explícito no código, e de uma forma que seja segura (mais detalhes abaixo).

Uma das razões pelas quais o uso cruzado é um erro é que os tamanhos da palavras pode ser diferente. Por exemplo, numa máquina com arquitetura AMD64 (todos os nossos PCs de 64 bits), ponteiros têm 64 bits, mas inteiros têm apenas 32 (se você quiser trabalhar com inteiros de 64 bits, têm de usar explicitamente long). O contrário também seria possível (i.e. uma arquitetura em que ponteiros têm menos bits do que o tamanho do maior inteiro possível).

Isso, de cara, já torna o printf() acima perigoso, pois um valor de 64 bits vai ser exibido como se fosse apenas de 32, o que significa que alguns bits serão simplesmente suprimidos no valor impresso.

Outra razão pela qual o uso cruzado é perigoso é que algumas arquiteturas não aceitam qualquer valor inteiro como ponteiro, principalmente por questões de alinhamento. Por exemplo, uma máquina que só consegue endereçar a memória em blocos de 4 bytes (não é necessariamente o caso dos nossos PCs, mas tais arquiteturas existem) só podem ter ponteiros cujo valor, convertido em inteiro, é necessariamente um múltiplo de 4. Se você atribuir um valor inteiro que não é múltiplo de 4 a um ponteiro e tentar usar tal ponteiro para obter um dado, o processador provavelmente vai gerar uma exceção de erro de barramento.

Outro risco envolvido com o uso cruzado é que a aritmética de ponteiros garante um funcionamento coerente quando você usa o ponteiro para apontar para uma sequência de elementos (arrays, vetores, matrizes etc.). Se você usar um inteiro para apontar para um array, será responsabilidade sua saber o tamanho de cada elemento caso queira percorrer os elementos desse array e, além disso, terá de usar uma sintaxe mais complicada.

Fora os problemas com ponteiros em si, a ideia de substituir o valor de uma execução em execuções posteriores comete o erro grosseiro de assumir que determinada variável vai sempre ocupar a mesma posição na memória. Isso pode até ser verdade num determinado sistema ou ambiente de execução, mas não é uma verdade absoluta (colocar isso numa aula é um absurdo, que mostra que o cara não tem a menor ideia do que está realmente falando, embora fale com grande eloquência). Veja a saída de seis execuções consecutivas do primeiro programa na minha máquina, deixando claro que a variável muda de lugar.

$ ./a.out 
valor x= -994010612
$ ./a.out
valor x= -1212030180
$ ./a.out
valor x= -1080773204
$ ./a.out
valor x= 339011036
$ ./a.out
valor x= 1587733628
$ ./a.out
valor x= 1999101548


-----------------------

Intercambiar inteiros e ponteiros tem seu valor e é necessário em alguns poucos casos.

Nos casos em que é necessário, existem algumas ajudas dadas pelo padrão do C.

A primeira dessas ajudas é a definição de um tipo auxiliar de inteiro que garante uma quantidade de bits necessária para armazenar seguramente um ponteiro. Tal se chama intptr_t, e é definido em <stdint.h> (disponível a partir do padrão ISO de 1999 para a linguagem C).

Também há uma forma segura de fazer conversões. Essa forma é indicar todas as conversões explicitamente no código por meio dos operadores de coerção de tipos (type cast).

int i, *pi;
intptr_t i_pi;
double *pd;

i=1234567890;
pi=&i;

i_pi=(intptr_t)pi; /* Explicito conversão de ponteiro para inteiro. */

pd=(double *)i_pi; /* Explicito conversão de inteiro para ponteiro. */
/*
No caso, essa conversão provavelmente vai me causar
problemas quando eu tentar acesso a *pd, pois normalmente
sizeof(double)>sizeof(int).
*/


Outro exemplo.

/*
Código em DOS para acesso direto à memória de vídeo (não
vai funcionar no Linux ou no Windows, que protegem o sistema
de tentativas de acesso direto e arbitrário à memória).
*/
struct screen_character {
char character;
uint8_t attribute;
};

struct screen_character *screen=
(struct screen_character *)0xb8000
; /* Ou 0xb8000000, dependendo da implementação. */

/*
Agora ‘screen’ pode ser usado como um array, por exemplo,

screen[0].attribute=ATTR_BLINK!ATTR_BLUE_FG|ATTR_RED_BG;
screen[0].character='P';
*/


Para imprimir o valor de um ponteiro, printf() tem uma conversão exclusiva, que é "%p". Se você quiser MUITO exibir o endereço como inteiro, o certo seria converter primeiro para intmax_t (também em <stdint.h>) e imprimir com a conversão "%jd" ou "%ju".











Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts