C++: Aritmética Avançada de Ponteiros

Última atualização: 2026-08-26

Na Aula 23, aprendemos o básico da aritmética de ponteiros (p + n, p - q).

Mas há muitos mais detalhes na aritmética de ponteiros — por exemplo, ponteiros podem ser comparados? O que é um ponteiro void*? O que são ponteiros para ponteiros?



1. Comparação de Ponteiros

(1) 1.1 Ponteiros Podem Ser Comparados?

Sim! Mas apenas comparações entre ponteiros que apontam para o mesmo array são significativas.

▶ Exemplo 2: Prática Básica de Programação (Dificuldade ⭐)

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p1 = &arr[0];
 int* p2 = &arr[3];
 
 if (p1 < p2) { // ✅ elemento apontado por p1 está antes de p2
 std::cout << "p1 está antes de p2" << std::endl;
 }
 
 if (p2 - p1 == 3) { // ✅ dois ponteiros estão a 3 elementos de distância
 std::cout << "p1 e p2 estão a 3 elementos de distância" << std::endl;
 }
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
p1 está antes de p2
p1 e p2 estão a 3 elementos de distância

💡 Ponto-chave: Comparações de ponteiros comparam valores de endereço (ou seja, posições dos elementos no array).


(2) 1.2 Comparação de Igualdade de Ponteiros

CPP
#include <iostream>

int main() {
 int x = 5;
 int* p1 = &x;
 int* p2 = &x;
 
 if (p1 == p2) { // ✅ dois ponteiros apontam para a mesma variável
 std::cout << "p1 e p2 apontam para a mesma variável" << std::endl;
 }
 
 int y = 10;
 int* p3 = &x;
 int* p4 = &y;
 
 if (p3 != p4) { // ✅ dois ponteiros apontam para variáveis diferentes
 std::cout << "p3 e p4 apontam para variáveis diferentes" << std::endl;
 }
 
 return 0;
}


2. Ponteiros void*

(1) 2.1 O que é um Ponteiro void*?

void* é um ponteiro genérico — ele pode apontar para qualquer tipo de dado.

CPP
#include <iostream>

int main() {
 int x = 5;
 double y = 3.14;
 char c = 'A';
 
 void* p1 = &x; // ✅ void* pode apontar para int
 void* p2 = &y; // ✅ void* pode apontar para double
 void* p3 = &c; // ✅ void* pode apontar para char
 
 return 0;
}

💡 Características:


(2) 2.2 Usos de void*

Uso 1: Escrever funções genéricas (mais tarde você aprenderá que std::memcpy usa void*)

CPP
#include <iostream>
#include <cstring>

int main() {
 int arr1[5] = {1, 2, 3, 4, 5};
 int arr2[5];
 
 // std::memcpy usa void* para aceitar ponteiro de qualquer tipo
 std::memcpy(arr2, arr1, 5 * sizeof(int));
 
 for (int i = 0; i < 5; i++) {
 std::cout << arr2[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Uso 2: Alocação dinâmica de memória (mais tarde você aprenderá que malloc retorna void*)


(3) 2.3 Convertendo void*

Para usar o valor apontado por void*, você deve convertê-lo para um ponteiro de um tipo concreto.

CPP
#include <iostream>

int main() {
 int x = 5;
 void* p = &x; // void* apontando para int
 
 // ❌ Erro: Não é possível desreferenciar void* diretamente
 // std::cout << *p << std::endl;
 
 // ✅ Correto: Primeiro converta para int*
 int* intPtr = static_cast<int*>(p);
 std::cout << *intPtr << std::endl; // Exibe 5
 
 return 0;
}

💡 Dica: static_cast é o operador de conversão de tipo do C++ (coberto mais tarde), que é mais seguro que o estilo C (int*)p.



3. Ponteiros Multinível (Ponteiros para Ponteiros)

(1) 3.1 O que é um Ponteiro para Ponteiro?

Um ponteiro para ponteiro é um ponteiro que armazena o endereço de outro ponteiro (bonecas russas).

CPP
#include <iostream>

int main() {
 int x = 5;
 int* p = &x; // p aponta para x
 int** pp = &p; // pp aponta para p (ponteiro para ponteiro)
 
 std::cout << "x = " << x << std::endl;
 std::cout << "*p = " << *p << std::endl; // Desreferência uma vez: obtém valor de x
 std::cout << "**pp = " << **pp << std::endl; // Desreferência duas vezes: obtém valor de x
 
 return 0;
}

(2) 3.2 Declarando Ponteiros Multinível

Declaração Significado
int x; x é um int
int* p; p é um int* (ponteiro para int)
int** pp; pp é um int** (ponteiro para int*)
int*** ppp; ppp é um int*** (ponteiro para int**)

💡 Como ler: Leia da direita para a esquerda.


(3) 3.3 Usos de Ponteiros Multinível

Uso 1: Arrays 2D dinâmicos (coberto mais tarde)

CPP
// Alocar dinamicamente um array 2D 3x4
int** matrix = new int*[3]; // matrix é um ponteiro para ponteiro
for (int i = 0; i < 3; i++) {
 matrix[i] = new int[4];
}

Uso 2: Funções que precisam modificar um ponteiro (raro, apenas esteja ciente)

CPP
#include <iostream>

void allocateInt(int** ptr) {
 *ptr = new int(5); // Modifica *ptr (ou seja, modifica o ponteiro original)
}

int main() {
 int* p = nullptr;
 allocateInt(&p); // Passa o endereço do ponteiro
 std::cout << *p << std::endl; // Exibe 5
 delete p; // Libera memória
 return 0;
}


4. Ponteiros e const: Combinações

(1) 4.1 Quatro Combinações

Declaração Significado Pode mudar o ponteiro? Pode mudar o valor apontado?
int* p; Ponteiro ordinário ✅ Sim ✅ Sim
const int* p; Ponteiro para int const ✅ Sim ❌ Não
int* const p = &x; Ponteiro const (deve inicializar) ❌ Não ✅ Sim
const int* const p = &x; Ponteiro const para const ❌ Não ❌ Não

💡 Truque de memorização: Leia da direita para a esquerda.


(2) 4.2 Exemplo: const int* (Comumente Usado)

CPP
#include <iostream>

void printArray(const int* arr, int size) {
 for (int i = 0; i < size; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 // ❌ Não pode modificar:arr[i] = 0; // Erro de compilação
}

int main() {
 int arr[5] = {1, 2, 3, 4, 5};
 printArray(arr, 5);
 return 0;
}

Saída:

TEXT 📖 Somente leitura
1 2 3 4 5

💡 Ponto-chave: const int* arr é o tipo de parâmetro de função mais comumente usado — significa "eu apenas leio, não modifico."



5. Prática: Implementando Cópia de String com Ponteiros

▶ Exemplo 1: strcpy feito à mão (Dificuldade ⭐⭐)

CPP
#include <iostream>

void myStrcpy(char* dest, const char* src) {
 while (*src != '\0') {
 *dest = *src; // Copia caractere
 dest++; // Move ponteiro
 src++;
 }
 *dest = '\0'; // Adiciona terminador nulo
}

int main() {
 char src[] = "Hello";
 char dest[20];
 
 myStrcpy(dest, src);
 std::cout << "src = " << src << std::endl;
 std::cout << "dest = " << dest << std::endl;
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
src = Hello
dest = Hello

💡 Dica: Este programa usa aritmética de ponteiros e desreferência — é uma prática excelente para entender ponteiros.


▶ Exemplo 3: Percorrendo um Array com Ponteiros (Dificuldade ⭐)

CPP
#include <iostream>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int* p = arr; // p aponta para o primeiro elemento do array

    std::cout << "Percorrendo array com ponteiros: " << std::endl;
    for (int i = 0; i < 5; i++) {
        std::cout << "*p = " << *p << std::endl;
        p++; // Move ponteiro para o próximo elemento
    }

    // subtração de ponteiros calcula número de elementos
    int* end = arr + 5;
    std::cout << "Número de elementos:" << end - arr << std::endl;

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Percorrendo array com ponteiros: 
*p = 10
*p = 20
*p = 30
*p = 40
*p = 50
Número de elementos:5

❓ Perguntas Frequentes

P: Por que void não pode ser desreferenciado?* R: Porque void é um tipo sem tamanho — o compilador não sabe se deve ler 1 byte, 4 bytes, ou 8 bytes.

Ele deve ser convertido para um ponteiro de um tipo concreto (como int*, double*) antes que o compilador saiba o tamanho do passo.

P: Para que servem os ponteiros multinível? R: Em projetos reais, int** é mais comumente visto em: > 1. Arrays 2D dinâmicos (ex: new int*[3]) > 2. Funções que precisam modificar o ponteiro em si (ex: void foo(int** p) pode modificar para onde p aponta)

Ponteiros além de dois níveis (int*** etc.) não são recomendados — muito difíceis de entender.

P: const int* e int const* são iguais? R: Sim! As duas formas seguintes são equivalentes: > > const int p; // Recomendado > int const p; // Equivalente, mas menos comum > > > Mas int* const p é diferente — este é um ponteiro const (não pode mudar para onde aponta).**


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Declare uma variável int x = 5, depois declare um ponteiro int* p apontando para x, então declare um ponteiro int** pp apontando para p. Exiba x, *p, **pp e verifique se são iguais.

  2. Intermediário (Dificuldade ⭐⭐): Escreva uma função void swap(int** a, int** b) que troque para onde dois ponteiros apontam.

TEXT 📖 Somente leitura
Entrada: p1 aponta para x, p2 aponta para y
Chame swap(&p1, &p2)
Saída: p1 aponta para y, p2 aponta para x
  1. Desafio (Dificuldade ⭐⭐⭐): Use void* e aritmética de ponteiros para escrever à mão uma função myMemcpy:
  2. Protótipo da função: void myMemcpy(void* dest, const void* src, size_t n)
  3. Funcionalidade: copiar n bytes de src para dest
  4. Dica: copie byte a byte (use char* para aritmética de ponteiros)

6. 🚀 Próximo Passo

Agora que você aprendeu conceitos avançados de ponteiros, vamos avançar para Referências (Aula 25) — a "versão mais segura" dos ponteiros, um recurso importante do C++!

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%