Tentando ser um pouco mais didático…
Toda variável é alocada em algum lugar da memória. Então ao fazer uma declaração como int a
, é reservado um espaço para variável a
. E toda região da memória possui um endereço (geralmente um número gigante, tipo 0x7ffe39dc53ac
, mas neste exemplo, para simplificar, vamos usar números menores).
Então primeiro temos int a
, que declara a variável a
, aloca um espaço da memória para ela (vamos supor que seja o endereço de memória 42), mas não coloca nenhum valor ainda. Então temos:
variável |
endereço |
valor |
a |
42 |
— |
Depois o programa declara int *p
(um ponteiro para int
). Ponteiros são endereços de memória, mas é importante lembrar que, por serem variáveis, eles também estão em algum lugar da memória (afinal, ele precisa de um lugar para armazenar o endereço). Então vamos supor que p
foi alocado no endereço 50:
variável |
endereço |
valor |
a |
42 |
— |
p |
50 |
— |
Não confundir o endereço em que p
está alocado com o endereço que ele contém (o valor dele). Se ficou confuso, continue lendo, que isso será esclarecido
Enfim, depois declaramos mais dois ponteiros: int *b
e int *c
. Vamos supor que eles foram alocados nos endereços 60 e 70:
variável |
endereço |
valor |
a |
42 |
— |
p |
50 |
— |
b |
60 |
— |
c |
70 |
— |
Como nenhuma variável teve um valor atribuído, então elas estão sem valor (daí o traço).
Agora que começa a parte interessante. Ao fazer p = &a;
, estamos dizendo que o valor do ponteiro p
será o endereço da variável a
(pois é isso que o operador &
faz, pega o endereço de algo). Ou seja:
variável |
endereço |
valor |
a |
42 |
— |
p |
50 |
42 |
b |
60 |
— |
c |
70 |
— |
Repare que o valor de p
é 42, que é o endereço no qual a
está. Dizemos então que “p
aponta para a
” (ou “p
é um ponteiro para a
”, ou “p
tem o endereço de a
”, etc).
E como já dito antes, não confundir o endereço no qual p
está alocado (onde ele está na memória, no caso, o endereço 50) com o endereço que ele contém (o valor do ponteiro, o endereço para o qual ele aponta, no caso, 42).
Na linha seguinte temos *p = 2;
. O operador *
serve para “desreferenciar” (nem sei se essa palavra existe) um ponteiro. Portanto esta linha está dizendo: “Pegue o valor 2
e coloque no endereço para o qual p
está apontando”. Eu não estou mudando o endereço para o qual p
aponta (ele vai continuar apontando para o endereço 42). O que estou mudando é o valor que está no endereço 42. Ou seja, depois desta linha ficará assim:
variável |
endereço |
valor |
a |
42 |
2 |
p |
50 |
42 |
b |
60 |
— |
c |
70 |
— |
Por isso que na linha seguinte, ao imprimir o valor de a
, é mostrado 2
.
Depois temos b = p;
. Aqui é uma atribuição de ponteiros, que na prática faz com que ambos apontem para o mesmo endereço. O que ele faz basicamente é “pegue o endereço para o qual p
aponta, e faça b
apontar para ele também”. E como p
está apontando para o endereço 42, então depois desta linha, b
também estará:
variável |
endereço |
valor |
a |
42 |
2 |
p |
50 |
42 |
b |
60 |
42 |
c |
70 |
— |
O mesmo vale para a linha seguinte: c = b;
. Ela diz para pegar o endereço para o qual b
está apontando (no caso, 42) e fazer com que c
também aponte para ele:
variável |
endereço |
valor |
a |
42 |
2 |
p |
50 |
42 |
b |
60 |
42 |
c |
70 |
42 |
E por fim, temos *c = 10;
, que usa o operador de desreferência. Ele diz para pegar o valor 10
e colocar no endereço para o qual c
aponta (no caso, é o endereço 42). Então depois desta linha, fica assim:
variável |
endereço |
valor |
a |
42 |
10 |
p |
50 |
42 |
b |
60 |
42 |
c |
70 |
42 |
E por isso que ao imprimir a
, agora mostra 10
.