C++: Ponteiros e Arrays
Última atualização: 2026-08-26
Na Aula 16, aprendemos sobre arrays, mas não discutimos o que um nome de array realmente é.
Na verdade, um nome de array é um ponteiro — ele armazena o endereço do primeiro elemento do array.
Entender isso ajuda a explicar por que arrays "decaem para ponteiros" quando passados como parâmetros.
1. O Nome do Array é um Ponteiro
(1) 1.1 O Fato Básico
Um nome de array é um ponteiro para o primeiro elemento.
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
std::cout << "arr = " << arr << std::endl;
std::cout << "&arr[0] = " << &arr[0] << std::endl;
// Ambos exibem o mesmo! Porque arr é &arr[0]
return 0;
}
Saída (exemplo):
arr = 0x7ffd4a3b
&arr[0] = 0x7ffd4a3b
💡 Ponto-chave: arr e &arr[0] são o mesmo endereço.
2. Acessando Elementos do Array com Ponteiros
(1) 2.1 Uso Básico
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p = arr; // p aponta para o primeiro elemento
// Método 1: Subscrito(mesmo que array)
std::cout << "arr[0] = " << p[0] << std::endl; // 10
std::cout << "arr[1] = " << p[1] << std::endl; // 20
// Método 2: Desreferência
std::cout << "*p = " << *p << std::endl; // 10
std::cout << "*(p+1) = " << *(p+1) << std::endl; // 20
return 0;
}
💡 Ponto-chave: p[i] é na verdade *(p+i) (aritmética de ponteiros será explicada mais tarde).
3. Aritmética de Ponteiros
(1) 3.1 Adição/Subtração de Inteiros a Ponteiros
Ao adicionar ou subtrair um inteiro de um ponteiro, não se simplesmente adiciona/subtrai o valor do endereço — adiciona/subtrai inteiro × sizeof(tipo) bytes.
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p = arr; // Apontando para arr[0]
std::cout << "p = " << p << std::endl; // Assume 0x1000
std::cout << "p + 1 = " << p + 1 << std::endl; // 0x1004(Não 0x1001!)
std::cout << "p + 2 = " << p + 2 << std::endl; // 0x1008
return 0;
}
Como funciona:
intocupa 4 bytesp + 1na verdade adiciona1 × 4 = 4bytes- Então
p + 1aponta paraarr[1]
(2) 3.2 Subtração de Ponteiros
Subtrair dois ponteiros retorna a distância entre eles (número de elementos), não o número de bytes.
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p1 = &arr[0]; // Apontando para arr[0]
int* p2 = &arr[3]; // Apontando para arr[3]
std::cout << "p2 - p1 = " << p2 - p1 << std::endl; // 3(Número de elementos)
return 0;
}
💡 Ponto-chave: Subtrair ponteiros só faz sentido quando eles apontam para elementos do mesmo array.
4. Percorrendo um Array com Ponteiros (Três Métodos)
▶ Exemplo 1: Percorrendo um Array (Dificuldade ⭐)
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
// Método 1: Subscrito(Mais comum)
std::cout << "Método 1: Subscrito" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// Método 2: Aritmética de ponteiros
std::cout << "Método 2: Aritmética de ponteiros" << std::endl;
for (int* p = arr; p < arr + 5; p++) {
std::cout << *p << " ";
}
std::cout << std::endl;
// Método 3: Usar função da biblioteca padrão (aprenderemos mais tarde)
// std::cout << "Método 3: std::begin/end" << std::endl;
// for (int* p = std::begin(arr); p != std::end(arr); p++) {
// std::cout << *p << " ";
// }
return 0;
}
Saída:
Método 1: Subscrito
10 20 30 40 50
Método 2: Aritmética de ponteiros
10 20 30 40 50
💡 Recomendação: O Método 1 (subscrito) é o mais intuitivo — prefira-o. O Método 2 (aritmética de ponteiros) é comum em código de baixo nível.
5. Decaimento do Array Quando Passado como Parâmetro
(1) 5.1 O Problema: Arrays Decaem para Ponteiros
Quando você passa um array para uma função, ele decai para um ponteiro (perdendo informação de tamanho).
#include <iostream>
void printSize(int arr[]) {
// arr é na verdade um ponteiro, não um array!
std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 8(sistema de 64 bits, tamanho do ponteiro)
}
int main() {
int arr[5] = {10, 20, 30, 40, 50};
std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20(5 x 4 = 20 bytes)
printSize(arr);
return 0;
}
💡 Ponto-chave: Dentro da função printSize, arr é na verdade um ponteiro, então sizeof(arr) retorna o tamanho do ponteiro (8 bytes), não o tamanho do array (20 bytes).
(2) 5.2 Solução: Passar o Tamanho do Array
#include <iostream>
// Deve additionally passar o tamanho do array
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int arr[5] = {10, 20, 30, 40, 50};
printArray(arr, 5); // Passa nome do array (que é endereço do primeiro elemento)
return 0;
}
💡 Dica: Depois você aprenderá std::array e std::vector, que preservam informação de tamanho e são mais seguros.
6. Prática: Invertendo um Array
▶ Exemplo 2: Invertendo um Array com Ponteiros (Dificuldade ⭐⭐)
#include <iostream>
void reverseArray(int* arr, int size) {
int* left = arr; // Apontando para o primeiro elemento
int* right = arr + size - 1; // Apontando para o último elemento
while (left < right) {
// Troca os valores apontados por left e right
int temp = *left;
*left = *right;
*right = temp;
left++; // Move para a direita
right--; // Move para a esquerda
}
}
int main() {
int arr[5] = {10, 20, 30, 40, 50};
std::cout << "Antes da inversão: ";
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
reverseArray(arr, 5);
std::cout << "Depois da inversão: ";
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
Saída:
Antes da inversão: 10 20 30 40 50
Depois da inversão: 50 40 30 20 10
7. Erros Comuns
(1) 7.1 Ponteiro Fora dos Limites
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p = arr;
std::cout << *(p + 10) << std::endl; // ❌ Fora dos limites! p+10 aponta fora do array
// Exibe valor lixo, ou o programa falha
return 0;
}
💡 Dica: C++ não verifica automaticamente os limites de ponteiros! Isso é uma razão pela qual C++ é rápido, mas também uma fonte comum de bugs.
(2) 7.2 Subtração de Ponteiros Sem Sentido
#include <iostream>
int main() {
int x = 5;
int y = 10;
int* p1 = &x;
int* p2 = &y;
std::cout << p2 - p1 << std::endl; // ❌ Comportamento indefinido! p1 e p2 apontam para arrays diferentes
// Só subtrair ponteiros do mesmo array faz sentido
return 0;
}
❓ Perguntas Frequentes
P: Por que
arre&arrtêm o mesmo valor mas tipos diferentes? R:> -arrtem tipoint*(ponteiro paraint) > -&arrtem tipoint(*)[5](ponteiro para "array de 5 ints")Então seus valores (endereços) são iguais, mas seus tipos diferem, o que significa que os passos da aritmética de ponteiros são diferentes:
CPPint arr[5] = {10, 20, 30, 40, 50}; int* p1 = arr; // p1 + 1 + 4 bytes int (*p2)[5] = &arr; // p2 + 1 + 20 bytes(5 × 4)
P: Por que
sizeof(arr)é 20 emmainmas 8 em uma função? R: Porque emmain,arré um array; dentro de uma função,arrjá decaiu para um ponteiro.Solução: Use
std::arrayoustd::vector(coberto mais tarde).
P: A aritmética de ponteiros pode usar
*e/? R: Não! Ponteiros só podem fazer: > - Adição/subtração de inteiros (p + n,p - n) > - Subtração de dois ponteiros (p2 - p1) > - Comparação (p1 < p2)Não pode fazer:
p1 * p2,p1 / p2,p1 + p2.
▶ Exemplo 3: Percorrendo um Array com Ponteiros (Dificuldade ⭐)
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int* p = arr;
for (int i = 0; i < 5; i++) {
std::cout << "*(p+" << i << ")=" << *(p + i) << std::endl;
}
return 0;
}
Saída:
*(p+0)=10
*(p+1)=20
*(p+2)=30
*(p+3)=40
*(p+4)=50
*(p + i) é equivalente a p[i] ou arr[i]. Adicionar/subtrair inteiros de ponteiros considera automaticamente o tamanho do tipo.
- Nomes de arrays são ponteiros (apontando para o endereço do primeiro elemento)
- Adição/subtração de inteiros a ponteiros adiciona
inteiro × sizeof(tipo)bytes - Subtrair dois ponteiros retorna o número de elementos entre eles, não bytes
- Arrays decaem para ponteiros quando passados como parâmetros; você precisa passar o tamanho separadamente
- Há três métodos para percorrer arrays com ponteiros: subscrito, aritmética de ponteiros, funções da biblioteca padrão
📖 Resumo
- Nome do array: endereço do primeiro elemento, equivalente a um ponteiro
- Percurso com ponteiros:
*(p + i)é equivalente ap[i] - Array como parâmetro: decai para ponteiro, requer passar o comprimento separadamente
- Arrays multidimensionais: percorridos usando ponteiros para arrays
📝 Exercícios
-
Básico (Dificuldade ⭐): Declare um array
intarr[5] = {1, 2, 3, 4, 5}, depois declare um ponteiroint*papontando paraarr. Exiba todos os elementos usando o ponteiro. -
Intermediário (Dificuldade ⭐⭐): Escreva uma função
int* findElement(int* arr, int size, int target)que procure um valor alvo em um array. Se encontrado, retorne um ponteiro para esse elemento; se não encontrado, retornenullptr. -
Desafio (Dificuldade ⭐⭐⭐): Escreva um programa que implemente "bubble sort" usando ponteiros:
-
Percorra o array, comparando elementos adjacentes
-
Se estiverem na ordem errada, troque-os
-
Repita esse processo até que o array esteja ordenado
- Nomes de arrays são ponteiros constantes (endereço do primeiro elemento)
- Aritmética de ponteiros: p + n desloca por n elementos, não n bytes
- Acesso por subscrito do array arr[i] é equivalente a *(arr + i)
- Percorrer arrays com ponteiros é mais flexível do que com subscritos
- Arrays decaem para ponteiros quando passados como parâmetros de função
8. 🚀 Próximo Passo
Agora que você aprendeu sobre ponteiros e arrays, vamos avançar para Aritmética Avançada de Ponteiros (Aula 24) — uma visão mais profunda de tipos de ponteiros, ponteiros void*, e ponteiros multinível.