🧮 Aritmética de Ponteiros · Operações com Endereços · C/C++

🧮 Aritmética de Ponteiros

"Aritmética de ponteiros é o conjunto de operações que permitem manipular endereços de memória. Quando você adiciona 1 a um ponteiro, ele avança para o próximo elemento do tipo apontado, não necessariamente 1 byte."

A aritmética de ponteiros é uma das características mais poderosas — e perigosas — de C/C++. Ela permite percorrer arrays, manipular strings e acessar estruturas de dados de forma extremamente eficiente. Compreender como os endereços são calculados é essencial para usar ponteiros corretamente.

📌 O Básico: Ponteiro + Inteiro

Quando você adiciona um inteiro n a um ponteiro, o endereço resultante é deslocado por n * sizeof(tipo) bytes.

int arr[5] = {10, 20, 30, 40, 50}; int* p = arr; // p aponta para arr[0] cout << *p << endl; // 10 cout << *(p + 1) << endl; // 20 (p + 1 aponta para arr[1]) cout << *(p + 2) << endl; // 30 (p + 2 aponta para arr[2]) // O deslocamento em bytes é automático // Se sizeof(int) = 4, p + 1 adiciona 4 bytes ao endereço

📊 Visualização do Deslocamento

ExpressãoEndereço (exemplo)Valor
p (p + 0)0x100010
p + 10x100420
p + 20x100830
p + 30x100C40
p + 40x101050

🔢 Operações Válidas com Ponteiros

OperaçãoExemploResultado
Ponteiro + Inteirop + 3Endereço do 4º elemento
Inteiro + Ponteiro3 + pMesmo que p + 3
Ponteiro - Inteirop - 1Endereço do elemento anterior
Ponteiro - Ponteiroq - pNúmero de elementos entre eles
Incrementop++ ou ++pAvança para o próximo elemento
Decrementop-- ou --pRetrocede para o elemento anterior
⚠️ Operações Inválidas:
  • Ponteiro + Ponteiro (não faz sentido)
  • Ponteiro * Ponteiro
  • Ponteiro / Ponteiro
  • Ponteiro % Ponteiro

📈 Incremento e Decremento

Os operadores ++ e -- são frequentemente usados para percorrer arrays.

int arr[5] = {10, 20, 30, 40, 50}; int* p = arr; // Usando incremento para percorrer for (int i = 0; i < 5; i++) { cout << *p << " "; p++; // Avança para o próximo elemento } // Usando ponteiro diretamente no loop for (int* p = arr; p < arr + 5; p++) { cout << *p << " "; } // Saída: 10 20 30 40 50 // Cuidado com a ordem: *p++ vs (*p)++ p = arr; cout << *p++ << endl; // 10 (depois p aponta para arr[1]) cout << *p << endl; // 20 p = arr; cout << (*p)++ << endl; // 10 (incrementa o valor, arr[0] agora é 11) cout << arr[0] << endl; // 11

➖ Subtração de Ponteiros

A diferença entre dois ponteiros do mesmo tipo retorna o número de elementos entre eles (não o número de bytes).

int arr[10] = {0}; int* p1 = &arr[2]; int* p2 = &arr[7]; cout << p2 - p1 << endl; // 5 (número de elementos entre eles) cout << p1 - p2 << endl; // -5 // Cálculo do tamanho de um array usando ponteiros int* inicio = arr; int* fim = arr + 10; cout << fim - inicio << endl; // 10 (número de elementos)
💡 Ponteiros devem apontar para o mesmo array: A subtração de ponteiros só é definida quando ambos apontam para elementos do mesmo array (ou um após o final). Caso contrário, o comportamento é indefinido.

📏 Aritmética com Diferentes Tipos

O deslocamento em bytes depende do tamanho do tipo apontado.

char* pc = (char*)0x1000; int* pi = (int*)0x1000; double* pd = (double*)0x1000; pc++; // pc agora é 0x1001 (char = 1 byte) pi++; // pi agora é 0x1004 (int = 4 bytes) pd++; // pd agora é 0x1008 (double = 8 bytes)

📊 Tamanhos Típicos e Deslocamentos

Tiposizeofp + 1 desloca
char1 byte1 byte
short2 bytes2 bytes
int4 bytes4 bytes
long long8 bytes8 bytes
float4 bytes4 bytes
double8 bytes8 bytes

🔄 Aritmética com void*

Ponteiros void* não permitem aritmética diretamente, pois o compilador não sabe o tamanho do tipo apontado. É necessário converter para um tipo específico primeiro.

int arr[5]; void* p = arr; // p++; // ERRO: não pode fazer aritmética com void* // p = p + 1; // ERRO // Solução: converter para um tipo específico int* pi = static_cast<int*>(p); pi++; // OK

⚖️ Comparação de Ponteiros

Ponteiros podem ser comparados usando operadores relacionais (==, !=, <, >, <=, >=).

int arr[5] = {10, 20, 30, 40, 50}; int* inicio = arr; int* fim = arr + 5; // Percorrendo com comparação de ponteiros for (int* p = inicio; p < fim; p++) { cout << *p << " "; } // Verificando se um ponteiro está dentro de um intervalo int* p = &arr[3]; if (p >= inicio && p < fim) { cout << "Ponteiro dentro do array" << endl; }
⚠️ Cuidado com comparações entre arrays diferentes: Comparar ponteiros que apontam para arrays diferentes (exceto igualdade/desigualdade) resulta em comportamento indefinido.

📝 Exemplo Prático: Implementando strlen

Aritmética de ponteiros é a base para implementar funções de manipulação de strings.

size_t meu_strlen(const char* str) { const char* p = str; while (*p != '\0') { p++; // Avança até encontrar o terminador nulo } return p - str; // Diferença entre os ponteiros = comprimento } int main() { const char* msg = "Olá, mundo!"; cout << meu_strlen(msg) << endl; // 11 }

📋 Exemplo Prático: Copiando Arrays

void copiarArray(int* destino, const int* origem, size_t tamanho) { const int* fim = origem + tamanho; while (origem < fim) { *destino++ = *origem++; } } int main() { int fonte[5] = {1, 2, 3, 4, 5}; int destino[5]; copiarArray(destino, fonte, 5); }

💡 Boas Práticas

  • Prefira iteradores a ponteiros brutos em C++ moderno (mais seguro e genérico).
  • Verifique limites ao fazer aritmética — nunca ultrapasse o final do array.
  • Use nullptr para ponteiros que não apontam para nada válido.
  • Evite aritmética com void* — converta para o tipo correto primeiro.
  • Documente quando uma função espera um intervalo de ponteiros (início e fim).

🔗 Conclusão

A aritmética de ponteiros é uma ferramenta poderosa que permite manipular memória de forma eficiente e expressiva. Compreender como os deslocamentos funcionam, as operações válidas e as armadilhas comuns é essencial para escrever código C/C++ robusto. Embora em C++ moderno muitos desses padrões sejam encapsulados por iteradores e containers da STL, o conhecimento de aritmética de ponteiros permanece fundamental para entender o que acontece "sob o capô".

⏭️ Próximo: Ponteiros e const...

Postar um comentário

0 Comentários