Como sabemos, um ponteiro é usado para armazenar o endereço de uma variável em C. O ponteiro reduz o tempo de acesso de uma variável. Porém, em C, também podemos definir um ponteiro para armazenar o endereço de outro ponteiro. Esse ponteiro é conhecido como ponteiro duplo (ponteiro para ponteiro). O primeiro ponteiro é usado para armazenar o endereço de uma variável, enquanto o segundo ponteiro é usado para armazenar o endereço do primeiro ponteiro. Vamos entender isso pelo diagrama abaixo.
A sintaxe para declarar um ponteiro duplo é fornecida abaixo.
int **p; // pointer to a pointer which is pointing to an integer.
Considere o seguinte exemplo.
#include void main () { int a = 10; int *p; int **pp; p = &a; // pointer p is pointing to the address of a pp = &p; // pointer pp is a double pointer pointing to the address of pointer p printf('address of a: %x ',p); // Address of a will be printed printf('address of p: %x ',pp); // Address of p will be printed printf('value stored at p: %d ',*p); // value stoted at the address contained by p i.e. 10 will be printed printf('value stored at pp: %d ',**pp); // value stored at the address contained by the pointer stoyred at pp }
Saída
address of a: d26a8734 address of p: d26a8738 value stored at p: 10 value stored at pp: 10
Exemplo de ponteiro duplo C
Vejamos um exemplo onde um ponteiro aponta para o endereço de outro ponteiro.
Como você pode ver na figura acima, p2 contém o endereço de p (fff2) e p contém o endereço da variável numérica (fff4).
#include int main(){ int number=50; int *p;//pointer to int int **p2;//pointer to pointer p=&number;//stores the address of number variable p2=&p; printf('Address of number variable is %x ',&number); printf('Address of p variable is %x ',p); printf('Value of *p variable is %d ',*p); printf('Address of p2 variable is %x ',p2); printf('Value of **p2 variable is %d ',*p); return 0; }
Saída
Address of number variable is fff4 Address of p variable is fff4 Value of *p variable is 50 Address of p2 variable is fff2 Value of **p variable is 50
P. Qual será o resultado do programa a seguir?
#include void main () { int a[10] = {100, 206, 300, 409, 509, 601}; //Line 1 int *p[] = {a, a+1, a+2, a+3, a+4, a+5}; //Line 2 int **pp = p; //Line 3 pp++; // Line 4 printf('%d %d %d ',pp-p,*pp - a,**pp); // Line 5 *pp++; // Line 6 printf('%d %d %d ',pp-p,*pp - a,**pp); // Line 7 ++*pp; // Line 8 printf('%d %d %d ',pp-p,*pp - a,**pp); // Line 9 ++**pp; // Line 10 printf('%d %d %d ',pp-p,*pp - a,**pp); // Line 11 }
Explicação
Na questão acima, a aritmética do ponteiro é usada com o ponteiro duplo. É definido um array de 6 elementos que é apontado por um array de ponteiro p. O array de ponteiros p é apontado por um ponteiro duplo pp. No entanto, a imagem acima dá uma breve ideia sobre como a memória está sendo alocada para o array a e o array de ponteiros p. Os elementos de p são os ponteiros que apontam para cada elemento do array a. Como sabemos que o nome do array contém o endereço base do array, portanto, ele funcionará como um ponteiro e o valor pode ser percorrido usando *(a), *(a+1), etc. , a[0] pode ser acessado das seguintes maneiras.
- a[0]: é a forma mais simples de acessar o primeiro elemento do array
- *(a): como armazenamos o endereço do primeiro elemento do array, podemos acessar seu valor usando um ponteiro indireto sobre ele.
- *p[0]: se a[0] for acessado usando um ponteiro p para ele, então podemos usar o operador indireto (*) no primeiro elemento da matriz de ponteiros p, ou seja, *p[0].
- **(pp): como pp armazena o endereço base do array de ponteiros, *pp fornecerá o valor do primeiro elemento do array de ponteiros que é o endereço do primeiro elemento do array de inteiros. **p fornecerá o valor real do primeiro elemento da matriz de inteiros.
Chegando ao programa, as linhas 1 e 2 declaram o array de inteiros e ponteiros relativamente. A linha 3 inicializa o ponteiro duplo para a matriz de ponteiros p. Conforme mostrado na imagem, se o endereço do array começar em 200 e o tamanho do inteiro for 2, então o array de ponteiros conterá os valores 200, 202, 204, 206, 208, 210. Vamos considerar que o o endereço base da matriz de ponteiros é 300; o ponteiro duplo pp contém o endereço da matriz de ponteiros, ou seja, 300. A linha número 4 aumenta o valor de pp em 1, ou seja, pp agora apontará para o endereço 302.
A linha número 5 contém uma expressão que imprime três valores, ou seja, pp - p, *pp - a, **pp. Vamos calculá-los cada um deles.
- pp = 302, p = 300 => pp-p = (302-300)/2 => pp-p = 1, ou seja, 1 será impresso.
- pp = 302, *pp = 202, a = 200 => *pp - a = 202 - 200 = 2/2 = 1, ou seja, 1 será impresso.
- pp = 302, *pp = 202, *(*pp) = 206, ou seja, 206 será impresso.
Portanto, como resultado da linha 5, a saída 1, 1, 206 será impressa no console. Na linha 6, *pp++ está escrito. Aqui, devemos notar que dois operadores unários * e ++ terão a mesma precedência. Portanto, pela regra da associatividade, será avaliado da direita para a esquerda. Portanto a expressão *pp++ pode ser reescrita como (*(pp++)). Visto que pp = 302, que agora se tornará 304. *pp dará 204.
Na linha 7, novamente é escrita a expressão que imprime três valores, ou seja, pp-p, *pp-a, *pp. Vamos calcular cada um deles.
- pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, ou seja, 2 será impresso.
- pp = 304, *pp = 204, a = 200 => *pp-a = (204 - 200)/2 = 2, ou seja, 2 será impresso.
- pp = 304, *pp = 204, *(*pp) = 300, ou seja, 300 será impresso.
Portanto, como resultado da linha 7, a saída 2, 2, 300 será impressa no console. Na linha 8, ++*pp está escrito. De acordo com a regra de associatividade, isso pode ser reescrito como (++(*(pp))). Como pp = 304, *pp = 204, o valor de *pp = *(p[2]) = 206 que agora apontará para a[3].
Na linha 9, novamente é escrita a expressão que imprime três valores, ou seja, pp-p, *pp-a, *pp. Vamos calcular cada um deles.
- pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, ou seja, 2 será impresso.
- pp = 304, *pp = 206, a = 200 => *pp-a = (206 - 200)/2 = 3, ou seja, 3 será impresso.
- pp = 304, *pp = 206, *(*pp) = 409, ou seja, 409 será impresso.
Portanto, como resultado da linha 9, a saída 2, 3, 409 será impressa no console. Na linha 10, +****pp está escrito. de acordo com a regra de associatividade, isso pode ser reescrito como (++(*(*(pp)))). pp = 304, *pp = 206, **pp = 409, ++**pp => *pp = *pp + 1 = 410. Em outras palavras, a[3] = 410.
Na linha 11, novamente é escrita a expressão que imprime três valores, ou seja, pp-p, *pp-a, *pp. Vamos calcular cada um deles.
- pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, ou seja, 2 será impresso.
- pp = 304, *pp = 206, a = 200 => *pp-a = (206 - 200)/2 = 3, ou seja, 3 será impresso.
- Na linha 8, **pp = 410.
Portanto como resultado da linha 9, a saída 2, 3, 410 será impressa no console.
Por fim, a saída do programa completo será dada como:
Saída
1 1 206 2 2 300 2 3 409 2 3 410