Ponteiros em C – Aritmética de Ponteiros
Aritmética de Ponteiros
Na lição anterior definimos o que são ponteiros, como declará-los e como realizar sua atribuição. Vamos agora abordar a aritmética de ponteiros em linguagem C.
Há duas operações aritméticas que podemos efetuar com ponteiros: adição de ponteiros e subtração de ponteiros. Além disso, também é possível comparar dois ponteiros entre si.
Suponha dois ponteiros, pont1 e pont2. pont1 aponta para um inteiro de 16 bits, cujo endereço é 5000. Vamos incrementar esse ponteiro (somar uma posição a ele) por meio do operador de incremento ++:
pont1++;
Qual será o valor contido em pont1 agora?
O valor em pont1 será o endereço 5002, e não 5001, como era de se esperar por conta do incremento, pois sempre que um ponteiro é incrementado (ou decrementado), ele irá apontar para a posição de memória do próximo elemento de seu tipo (ou para a posição anterior, no caso do decremento).
Como um inteiro de 16 bits possui 2 bytes, o valor dese ponteiro é incrementado em duas unidades, e não em apenas uma. Já ponteiros de caractere sempre são incrementados ou decrementados em uma unidade, coo na aritmética normal, pois caracteres ocupam apenas um byte na memória.
Ou seja, a aritmética de ponteiros é baseada em seu tipo declarado.
Da mesma forma, se usarmos um ponteiro de inteiro de 32 bits, ao incrementá-lo seu endereço (no exemplo) passará a ser o endereço 5004, 4 “unidades” à frente, pois 32 bits equivalem a 4 bytes (32 / 8 bits).
Portanto, a aritmética de ponteiros lida com os endereços apontados por eles, e não com o conteúdo das variáveis apontadas. Fique atento a esse detalhe!
Tamanho de uma variável ponteiro
O tamanho de uma variável é um aspecto muito importante para o programador. Precisamos saber quanto espaço na memória uma variável ocupa quando é utilizada, para que seja possível escrever programas enxutos e que executem com boa performance. Isso é particularmente importante quando desenvolvemos programas para dispositivos embarcados, os quais, no geral, contam com quantidade restrita de memória disponível.
Dependendo da plataforma, uma variável pode ter tamanhos que variam de 8 a 64 bits. Por exemplo, em uma plataforma de 32 bits, uma variável do tipo int ocupará o espaço de 4 bytes (32 / 8 = 4).
O código a seguir nos permite verificar o tamanho na memória utilizado por ponteiros de diferentes tipos, usando o operador sizeof:
#include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { setlocale(LC_ALL, ""); char caractere; int inteiro; double duplo; int *inteiro_ptr; char *caractere_ptr; double *duplo_ptr; caractere_ptr = &caractere; inteiro_ptr = &inteiro; duplo_ptr = &duplo; printf("Tamanho de um ponteiro de inteiro = %d valor = %p\n", sizeof(inteiro_ptr),inteiro_ptr); printf("Tamanho de um ponteiro de char = %d valor = %p\n", sizeof(caractere_ptr),caractere_ptr); printf("Tamanho de um ponteiro de double = %d valor = %p\n", sizeof(duplo_ptr),duplo_ptr); return 0; }
A tabela a seguir mostra os tamanhos típicos de tipos de dados, em bytes, encontrados na maioria dos sistemas:
Tipo de dado | Tamanho em bytes |
byte | 1 |
char | 1 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
Adição e subtração em ponteiros
Tomemos como exemplo a expressão a seguir:
pont1 = pont1 + 40;
Essa declaração faz com que o ponteiro pont1 passe a apontar para o quadragésimo elemento do tipo declarado para o ponteiro a partir da posição apontada atualmente por ele. A mesma ideia se aplica à subtração de ponteiros, porém obviamente com o ponteiro apontando para posição anterior na memória RAM do sistema.
Note que a nova posição (quadragésimo elemento no exemplo) depende do tipo declarado no ponteiro. Por exemplo, para um ponteiro de inteiro de 32 bits isso significa 40 posições x 4 bytes = 160 posições à frente (A0 em hexadecimal).
Não podemos efetuar outras operações com ponteiros além da adição e subtração, com multiplicação e divisão, e nem podemos usar operadores de deslocamento bit a bit.
Vejamos um código de exemplo que mostra as operações de adição e subtração de ponteiros:
#include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { setlocale(LC_ALL, " "); int a; a = 30; int *pont1; pont1 = &a; printf("Endereço de a: %p\n", pont1); pont1++; printf("Endereço do ponteiro incrementado: %p\n", pont1); pont1 = pont1 + 10; printf("Somando 40 ao ponteiro pont1: %p\n", pont1); pont1--; printf("Decrementando o ponteiro: %p\n", pont1); return 0; }
Podemos ver o resultado da execução desse código na figura a seguir:
Inicialmente, podemos ver que o ponteiro pont1 apontava para o endereço de memória 0x0060FF08 (expresso em hexadecimal), onde se localiza a variável a criada.
Após o incremento (pont1++), ele passa a apontar para a posição xFF0C, que se encontra C – 8 = 4 endereços à frente da posição inicial; lembre-se de que temos aqui um inteiro de 32 bits (32 / 8 = 4).
Então, somamos 10 ao ponteiro (pont1 = pont1 + 10), que passa a apontar para o endereço xFF34. Note que, em hexadecimal, 34 – 0C = 28, que convertido para decimal é igual a 10.
E, finalmente, decrementamos o ponteiro (pont1–), que finaliza apontando para o endereço de memória xFF30, 4 posições a menos do que no comando anterior.
Comparação de Ponteiros
Podemos efetuar a comparação de ponteiros usando os operadores de comparação padrão – apesar de esta técnica não ter tanta utilidade na prática.
O código a seguir mostra a comparação entre ponteiros:
#include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { setlocale(LC_ALL, ""); int vetor[] = {10, 30, 20}; int *p0 = vetor; int *p1 = vetor + 1; int *p2 = vetor + 2; int *p3 = p2; printf("p2 > p0: %d\n", p2 > p0); printf("p0 > p1: %d\n", p0 > p1); printf("p2 < p0: %d\n", p2 < p0); printf("p3 = p2: %d\n", p3 == p2); return 0; }
O resultado mostrado será o valor 1 para comparação verdadeira e 0 para comparação falsa, como podemos ver na figura a seguir:
Anterior: Ponteiros em C – Introdução
Próxima lição: Indireção múltipla de ponteiros
Olá, eu ϲurti muito esse post! Parabéns!!