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
clang
7.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
P
for um ponteiro, a expressão pega o valor que estiver no endereço queP
contém (ou seja, o mesmo que*P
- por isso que é considerado “equivalente”) - Se
P
não for um ponteiro, a expressão não faz sentido, pois é como se quiséssemos fazer*P
quandoP
não é um ponteiro (ou seja, estamos tentando pegar o valor de algo que não é um ponteiro). No meugcc
aqui 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 deP
mais 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
. SeP1
for 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 dep
e 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 meugcc
aqui 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
.