Alguem consegue me explicar oq acontece nessa expressão? **(&P), não sei se um sinal anula outro, porém testando em um programa, vi q **(&P) é equivalente a *P. Porém **(&P+1) não é equivalente a *P+1, por isso não consigo entender essa relação.
Há um certo “perigo” nisso. Não sei qual o compilador nem SO em que vc está trabalhando, muito menos qual é o seu código. Assumi que sejam três variáveis, uma inteira, um ponteiro para inteiro e um ponteiro para um ponteiro de inteiros. De orelhada, tenho praticamente certeza que não há nenhuma garantia que a alocação de memória seja feita de forma contígua, mas no gcc do MinGW (Windows), para o código abaixo, isso parece se aplicar. Mesmo que isso seja uma característica do compilador, você não deveria assumir isso de forma alguma. Outra coisa, isso não tem absolutamente nada a ver com um operador anular o outro. Eu não sei da onde vc tirou isso ai, parece mais prova de concurso ou exercício “idiota” (desculpa o termo kkk).
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 50;
int *b = &a;
int **c = &b;
printf( "a:\n" );
printf( " endereco: %p\n valor: %d\n", &a, a );
printf( "b:\n" );
printf( " endereco: %p\n valor: %p\n"
" valor da variavel apontada: %d\n", &b, b, *b );
printf( "c:\n" );
printf( " endereco: %p\n valor: %p\n"
" valor da variavel apontada: %p\n"
" valor da variavel apontada pelo ponteiro apontado: %d\n\n", &c, c, *c, **c );
printf( "**(&c): %p\n", **(&c) );
printf( "*c: %p\n", *c );
printf( "(&c+1): %p\n", (&c+1) );
printf( "**(&c+1): %d\n", **(&c+1) );
return 0;
}
Complementando a resposta acima, testei o mesmo código no Ubuntu com gcc 7.5 e **(&c + 1) dá erro (segmentation fault). Também testei em IDE’s online e o resultado foi:
- no IdeOne.com (com
gcc 8.3) também deu erro nesta mesma linha - no Repl.it (com
clang7.0), “funciona”
Ou seja, conforme já foi mencionado acima, “não há nenhuma garantia que a alocação de memória seja feita de forma contígua”. Mas vamos ao problema de fato:
A questão é: por que você acha que deveria ser equivalente? Antes de achar algo, você tem que entender o que significa.
Vamos por partes:
&P significa “o endereço de P”. E ao usar *(alguma_coisa), você está dizendo "o valor que está no endereço indicado por alguma_coisa". Então temos que:
-
*(&P)é o valor que está no endereço indicado por&P. E como&Pé o endereço deP, então*(&P)é o mesmo que o valor deP(o próprioP) - E portanto
**(&P)é o valor que está no endereço indicado por*(&P). Mas como*(&P)é equivalente aP, então**(&P)seria o mesmo que*P:- Se
Pfor um ponteiro, a expressão pega o valor que estiver no endereço quePcontém (ou seja, o mesmo que*P- por isso que é considerado “equivalente”) - Se
Pnão for um ponteiro, a expressão não faz sentido, pois é como se quiséssemos fazer*PquandoPnão é um ponteiro (ou seja, estamos tentando pegar o valor de algo que não é um ponteiro). No meugccaqui isso nem compilou (mas pode ser que outros compiladores aceitem, só que aí sabe-se lá o que acontece…)
- Se
Não é que um “anulou” o outro, na verdade um operador foi aplicado ao resultado do outro. O & pega o endereço de P, depois o segundo * pega o valor que está nesse endereço (que deve ser outro endereço), e por fim o primeiro * pega o valor que está neste outro endereço.
Agora, o que é &P + 1? É o endereço de P mais 1. Só que aritmética de ponteiros não é tão simples assim, e esse 1 na verdade nem sempre será 1 byte. Quando você soma 1 a &P, a quantidade de bytes adicionada será igual a sizeof(P). Para ver melhor como é isso, fiz o código abaixo:
int a = 50;
int *b = &a;
printf("%ld\n", sizeof(a));
printf( "endereço de a : %p\n", &a);
printf( "endereço de a + 1: %p\n", &a + 1);
printf("%ld\n", sizeof(b));
printf( "endereço de b : %p\n", &b);
printf( "endereço de b + 1: %p\n", &b + 1);
char c = 'x';
char *ptr = &c;
printf("%ld\n", sizeof(c));
printf( "endereço de c : %p\n", &c);
printf( "endereço de c + 1: %p\n", &c + 1);
printf("%ld\n", sizeof(ptr));
printf( "endereço de ptr : %p\n", &ptr);
printf( "endereço de ptr+1: %p\n", &ptr + 1);
Testei no Ubuntu, gcc 7.5. O resultado foi:
4
endereço de a : 0x7ffffaca4fa4
endereço de a + 1: 0x7ffffaca4fa8
8
endereço de b : 0x7ffffaca4fa8
endereço de b + 1: 0x7ffffaca4fb0
1
endereço de c : 0x7ffffaca4fa3
endereço de c + 1: 0x7ffffaca4fa4
8
endereço de ptr : 0x7ffffaca4fb0
endereço de ptr+1: 0x7ffffaca4fb8
Repare que quando eu faço &a + 1, ele soma 4 ao valor de &a (pois no meu ambiente sizeof(a) é 4). Mas quando eu faço &b + 1, ele soma 8, já que b é um ponteiro para int, e o sizeof(b) é 8. O mesmo acontece com c (que é um char, cujo sizeof é 1, e por isso &c + 1 soma 1 ao valor de &c). Mas ptr é um ponteiro para char e seu sizeof é 8.
Enfim, vamos destrinchar **(&P + 1):
-
(&P + 1)é o endereço dePmais 1 (seguindo a regra acima, que na verdade não soma necessariamente 1, e simsizeof(P)) -
*(&P + 1)pega o valor que está no endereço indicado por(&P + 1). Vamos chamar esse valor deP1 -
**(&P + 1)pega o valor que está no endereço indicado porP1. SeP1for um ponteiro, pega o valor do endereço que ele aponta, senão pode acontecer o que já mencionei acima (dependendo do compilador não compila, ou dá segmentation fault, ou “funciona”, etc)
Agora, e *P + 1? Isso pega o valor que está no endereço apontado por P e soma 1. Se P for, por exemplo, um ponteiro para int, então isso é o valor do int somado a 1. Não é nem de longe equivalente a **(&P + 1). Veja este exemplo:
int a = 1; // a é um inteiro
int *p = &a; // p é um ponteiro com o endereço de a
No código acima temos o ponteiro p, que aponta para o endereço de a.
Se fizermos *p, eu tenho o valor que está no endereço que p aponta. Ou seja, o valor de a, que é 1. Então *p + 1 é igual a 2.
Agora, se eu fizer **(&p + 1):
-
(&p + 1)pega o endereço depe soma 1 (e já vimos acima que na verdade ele somasizeof(p), e isso vai apontar para um endereço de memória que não necessariamente será válido - pode por coincidência apontar para algo válido, mas também pode ser que não) -
*(&p + 1)vai pegar o valor que está no endereço obtido acima (que pode ou não ser válido, então sabe-se lá o que pode vir aqui) -
**(&p + 1)pega o valor que está no “endereço” obtido no passo anterior (que pode nem ser um endereço de fato - no meugccaqui deu segmentation fault, em outros compiladores ou outras circunstâncias pode “funcionar” por coincidência ou devido a detalhes específicos de implementação, etc)
Por isso que **(&P+1) não é equivalente a *P+1.

