Continuando o assunto abordado no segundo artigo, sobre Aritmética de ponteiro, veremos algumas aplicações envolvendo aritmética de ponteiros e a sua relação com arrays. Em um dos exemplos mostrados no artigo anterior foi demonstrada a operação de incremento, evidenciando que ao incrementar um ponteiro, ele era deslocado para o próximo dado. Diante disso, veremos que há uma estreita relação entre ponteiros e arrays.
Ponteiros e Arrays
Arrays unidimensionais, ou vetores, são um conjunto de dados de mesmo tipo e que são armazenados em posições contíguas da memória [1]. Considere, por exemplo, um vetor de inteiros com 10 elementos armazenados a partir do endereço 108. No código abaixo é mostrado o exemplo ilustrado na Figura 1.
int v[] = {5, 10, 15, 3, 10, 76, 5, 13, 33, 45};
int * pt;
pt = v; //atribui o endereço do vetor
Um ponteiro é criado e passa a apontar para o primeiro elemento do vetor. Para atribuir o endereço de um vetor para um ponteiro basta utilizar o próprio nome do vetor, isto é, o nome representa o endereço do primeiro elemento.
Para obter o endereço de outro índice é necessário utilizar o operador ‘&’. Portanto, as duas atribuições mostradas abaixo são equivalentes.
pt = v; pt = &v[0];
Logo, o endereço do quinto elemento pode ser obtido da seguinte forma:
pt = &v[4];
Essa situação é ilustrada na Figura 2.
Diante disso, verifica-se que o elemento de um vetor é armazenado numa posição de memória na qual o seu endereço é equivalente à soma do endereço base com o total de bytes dos elementos até a posição desejada. Dito de outra maneira, se pt aponta para o endereço base do vetor então ‘V[n]’ é equivalente ‘*(pt + n)’. Na figura 3 é ilustrado essa alteração do endereço apontado.
Por exemplo, para exibir os 10 elementos desse vetor.
int v[] = {5, 10, 15, 3, 10, 76, 5, 13, 33, 45};
int * pt;
int i;
pt = v;
for(i = 0; i < 10; i++)
{
printf("V[%i] = %i\r\n", i, *(pt + i));
}
De fato, as duas formas de indexar os elementos do vetor apresentam o mesmo resultado, contudo a aritmética de ponteiros pode ser mais rápida do que a indexação direta, pois na indexação direta o endereço do elemento que será acessado é sempre calculado (somar o endereço base com a posição desejada) [1]. Uma pequena alteração no exemplo acima faz o ponteiro alterar o endereço conforme a iteração.
int v[] = {5, 10, 15, 3, 10, 76, 5, 13, 33, 45};
int * pt;
int i;
pt = v;
for(i = 0; i < 10; i++)
{
printf("V[%i] = %i\r\n", i, *pt++);
}
Outro exemplo com aritmética de ponteiros é mostrado abaixo.
char *pointer1 = &table[0]; char *pointer2 = &table[49]; *pointer1++ = *--pointer2;
Neste exemplo, dois ponteiros são utilizados numa operação de atribuição. O código assembly gerado (AVR 8 bits) para este código é mostrado abaixo.
LD R16,-Z ; Pre-decrement Z pointer and load data ST X+,R16 ; Store data and post increment
Esse exemplo mostra como os modos de endereçamento podem ser explorados utilizando operações com ponteiros. Note que as duas instruções utilizadas possuem as operações de incremento e decremento.
De modo geral:
- *(pt + i) é igual a V[i].
- (pt + i) é igual a &V[i].
Array de Ponteiros
Os ponteiros também podem ser declarados na forma de vetores. Considere o exemplo abaixo que define um vetor de ponteiros com 4 elementos e mais quatros vetores de 3 elementos.
int * pt [4]; //vetor de ponteiros do tipo inteiro
int v1[3] = {1, 2, 3}; //vetor 1 com três elementos
int v2[3] = {4, 5, 6}; //vetor 2 com três elementos
int v3[3] = {7, 8, 9}; //vetor 3 com três elementos
int v4[3] = {10, 11, 12}; //vetor 4 com três elementos
pt[0] = v1; //atribui o endereço do vetor1 para o ponteiro pt[0]
pt[1] = v2; //atribui o endereço do vetor2 para o ponteiro pt[1]
pt[2] = v3; //atribui o endereço do vetor3 para o ponteiro pt[2]
pt[3] = v4; //atribui o endereço do vetor4 para o ponteiro pt[3]
Esse exemplo é ilustrado na Figura 4.
Agora, é necessário lembrar que ao acessar os elementos pt[0], pt[1], pt[2] e pt[3], estaremos manipulando ponteiros. Para acessar os elementos de cada vetor a partir dos ponteiros basta utilizar o operador ‘*’ e indicar o índice desejado. Considere os casos abaixo:
- *pt[0] é o valor 1, pois estamos acessando o conteúdo do endereço 116, ou seja, v1[0];
- *pt[1] é o valor 4, pois estamos acessando o conteúdo do endereço 128, ou seja, v2[0];
- *pt[2] é o valor 7, pois estamos acessando o conteúdo do endereço 140, ou seja, v3[0];
- *pt[3] é o valor 10, pois estamos acessando o conteúdo do endereço 152, ou seja, v4[0].
Esse mesmo resultado pode ser obtido da seguinte forma:
- *(*(pt+0)) é o valor 1;
- *(*(pt+1)) é o valor 4;
- *(*(pt+2)) é o valor 7;
- *(*(pt+3)) é o valor 10.
A parte destacada em negrito define o elemento de ‘pt’ que está sendo acessado. Esse termo é o endereço do vetor que queremos acessar, portanto ao fazer o uso do operador * indicamos que o conteúdo desse endereço deve ser acessado. O acesso aos outros elementos é mostrado na Figura 5.
Conclusão
Nesse artigo foi demonstrado como acessar arrays utilizando ponteiros. Essa é uma técnica muito utilizada, pois é um meio mais rápido de acessar os elementos quando comparada com a indexação direta do vetor. Assim como no artigo anterior, destacou-se a importância do tipo de dado do ponteiro, já que as operações aritméticas realizadas dependem dessa informação. Outro caso demonstrado foi o de um array de ponteiros, caso semelhante ao de declaração de uma matriz de strings. Essas características podem aumentar a eficiência de rotinas e permitem estruturar melhor o código.
Referências
[1] – Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt. [2] – AVR035: Efficient C coding for AVRFonte da imagem destacada: https://listamaze.com/top-10-programming-languages-for-job-security/









Muito bom! Uma dúvida Fernando: A partir do momento que eu incremento o ponteiro, ele passa a ter o valor do próximo endereço de memória do array, mais eu gostaria de saber se tem alguma maneira de ler o endereço base do ponteiro original após ele ter sido incrementado, ou a única maneira mesmo é guardando o endereço base do ponteiro em uma variável antes de incrementá-lo? Exemplo: unsigned long Time[DUMP_SIZE]; unsigned long Data[DUMP_SIZE]; //Pointers for Time and Data Arrays unsigned long * time_pt = Time; unsigned long * data_pt = Data; void SaveData() { //Calc variables static unsigned long… Leia mais »
Olá, Gustavo. Neste caso, a operação de incremento sempre altera o conteúdo do ponteiro, pois o operador de incremento realiza a seguinte operação: data_pt++; –> data_pt = data_pt + 1; Assim, o endereço do ponteiro sempre é modificado. Portanto, o endereço base pode ser representado por uma constante ou por um ponteiro que não será modificado. No seu exemplo o ponteiro base_address é iniciado com: base_address = (long)&data_pt[0]; Que poderia ser trocado para &Data[0]. Esse endereço (&Data[0]) é constante, pois é o nome do vetor. Respondendo a sua pergunta, uma vez incrementado você modifica o endereço armazenado, logo perde a… Leia mais »
Muito Obrigado Fernando, vou atualizar o código e usar o &Data[0] que é constante.
Ola,
Preciso colocar esse código para funcionar: https://pastebin.com/zDM0Zxmi … Ele é parte de um software maior, mas a lógica é a mesma. Está dando falha de segmentação na linha 12, no printf(). Antes quebrava na atribuição da linha 10, mas dessa forma passa: matrix = (int**)ptr; … Alguém tem uma ideia?
Olá, vinifr. Você declarou matrix como um ponteiro para ponteiro. Na atribuição de matrix você não passou o endereço de um ponteiro….você atribui o endereço do vetor. No primeiro artigo tem uma demonstração de indireção dupla. int main() { char buf[10] = {‘0′,’1′,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9’}; char *ptr; int **matrix; ptr = buf; //conteúdo de ptr é o endereço de buf matrix = (int**)&ptr; //conteúdo de matrix é o endereço do ponteiro ptr printf(“%c %cn”, matrix[0][0], matrix[0][1]); return 0; } Cabe ressaltar que ao declarar como int, o vetor será acessado como inteiro e não char. Alterando matrix para char o acesso fica… Leia mais »
Ola Fernando,
Muito obrigado pela ajuda, realmente dá certo como você disse. Interessante, realmente usando int ele imprime “0 4” e não “0 1”. 😀
É que usando o ponteiro do tipo int o acesso a memória é realizado com base no tamanho desse tipo de dado. Neste caso, utilizando ponteiro char somente um byte é manipulado, já no ponteiro int são 4 bytes. Ponteiro char: matrix[0][0] -> endereço base matrix[0][1] -> endereço base + 1 Ponteiro int: matrix[0][0] -> endereço base matrix[0][1] -> endereço base + 4 / que é o valor 4 O problema é que ao fazer a operação como int você manipula os quatro bytes. No printf você leu o índice [0] mas na verdade leu o valor ‘0123’ e depois… Leia mais »
Ah, muito interessante! Obrigado pela explicação!