Minha opinião pessoal é que as figuras com setas apontando dessa maneira ou dificultam a compreensão dos ponteiros. Faz com que pareçam algumas entidades abstratas e misteriosas. Eles não são.
Como tudo no seu computador, ponteiros são números . O nome "ponteiro" é apenas uma maneira elegante de dizer "uma variável que contém um endereço".
Portanto, deixe-me agitar as coisas explicando como um computador realmente funciona.
Temos um int
, ele tem o nome i
e o valor 5. Isso é armazenado na memória. Como tudo armazenado na memória, ele precisa de um endereço ou não poderíamos encontrá-lo. Digamos que i
termina no endereço 0x12345678 e seu amigo j
com o valor 6 acaba logo após ele. Supondo que uma CPU de 32 bits em que int seja 4 bytes e ponteiros sejam 4 bytes, as variáveis são armazenadas na memória física como esta:
Address Data Meaning
0x12345678 00 00 00 05 // The variable i
0x1234567C 00 00 00 06 // The variable j
Agora queremos apontar para essas variáveis. Criamos um ponteiro para int int* ip1
, e um int* ip2
. Como tudo no computador, essas variáveis de ponteiro também são alocadas em algum lugar da memória. Vamos supor que eles acabem nos próximos endereços adjacentes na memória, imediatamente após j
. Definimos os ponteiros para conter os endereços das variáveis alocadas anteriormente: ip1=&i;
("copie o endereço de i para ip1") e ip2=&j
. O que acontece nas entrelinhas é:
Address Data Meaning
0x12345680 12 34 56 78 // The variable ip1(equal to address of i)
0x12345684 12 34 56 7C // The variable ip2(equal to address of j)
Então, o que obtivemos foram apenas alguns pedaços de 4 bytes de memória contendo números. Não há flechas místicas ou mágicas em nenhum lugar à vista.
De fato, apenas olhando para um despejo de memória, não podemos dizer se o endereço 0x12345680 contém um int
ou int*
. A diferença é como o nosso programa escolhe usar o conteúdo armazenado neste endereço. (A tarefa do nosso programa é realmente apenas dizer à CPU o que fazer com esses números.)
Então adicionamos mais um nível de indireção com int** ipp = &ip1;
. Novamente, temos apenas um pedaço de memória:
Address Data Meaning
0x12345688 12 34 56 80 // The variable ipp
O padrão parece familiar. Ainda outro pedaço de 4 bytes contendo um número.
Agora, se tivéssemos um despejo de memória da pequena RAM fictícia acima, poderíamos verificar manualmente onde esses ponteiros apontam. Examinamos o que está armazenado no endereço da ipp
variável e encontramos o conteúdo 0x12345680. Qual é, obviamente, o endereço onde ip1
está armazenado. Podemos ir para esse endereço, verificar o conteúdo e encontrar o endereço de i
; finalmente, podemos ir para esse endereço e encontrar o número 5.
Portanto, se pegarmos o conteúdo de ipp, *ipp
obteremos o endereço da variável ponteiro ip1
. Ao escrever *ipp=ip2
copiamos ip2 para ip1, é equivalente a ip1=ip2
. Em ambos os casos, teríamos
Address Data Meaning
0x12345680 12 34 56 7C // The variable ip1
0x12345684 12 34 56 7C // The variable ip2
(Estes exemplos foram dados para uma grande CPU endian)
ipp
quando defini-lo, assim que sua pergunta é completo ;-)