Arrays: declaração, acesso e limites
1. Introdução aos Arrays em C
Em Linguagem C, um array é uma estrutura de dados homogênea que armazena uma coleção de elementos do mesmo tipo em posições contíguas de memória. Diferentemente de declarar múltiplas variáveis individuais (como nota1, nota2, nota3...), um array permite gerenciar dezenas, centenas ou milhares de valores com uma única variável e um índice.
As principais vantagens incluem: código mais enxuto e legível, facilidade para percorrer dados com loops, e alocação sequencial na memória que favorece a eficiência. Este artigo aborda desde a declaração básica até os perigos do acesso fora dos limites, passando pela relação fundamental com ponteiros.
2. Declaração de Arrays
A sintaxe básica para declarar um array unidimensional é:
tipo nome[tamanho];
O tamanho deve ser uma expressão constante inteira positiva. Exemplos práticos:
int idades[10]; // array de 10 inteiros
float alturas[5]; // array de 5 floats
char nome[50]; // array de 50 caracteres (string)
Arrays multidimensionais seguem a mesma lógica, adicionando mais dimensões:
int matriz[3][4]; // 3 linhas, 4 colunas (12 elementos no total)
float cubo[2][3][2]; // 2x3x2 = 12 floats
Na memória, os elementos são armazenados em ordem row-major (linha por linha).
3. Inicialização de Arrays
A inicialização pode ser feita no momento da declaração. Exemplos:
int vetor[5] = {1, 2, 3, 4, 5}; // inicialização completa
int parcial[5] = {10, 20}; // {10, 20, 0, 0, 0}
int implicito[] = {100, 200, 300}; // tamanho deduzido: 3
Na inicialização parcial, os elementos não explicitados recebem zero. Para arrays multidimensionais:
int mat[2][3] = {{1, 2, 3}, {4, 5, 6}}; // completa
int mat2[2][3] = {{1}, {4}}; // {{1,0,0},{4,0,0}}
4. Acesso a Elementos do Array
Em C, a indexação é base-zero: array[0] é o primeiro elemento. O operador [] serve tanto para leitura quanto para escrita:
int valores[4] = {10, 20, 30, 40};
valores[2] = 35; // agora: {10, 20, 35, 40}
int x = valores[0]; // x = 10
Para arrays multidimensionais, acessamos cada dimensão separadamente:
int mat[2][3] = {{1,2,3},{4,5,6}};
mat[1][2] = 99; // altera o último elemento (linha 1, coluna 2)
Percorrer arrays com loops é uma operação comum:
int soma = 0;
for(int i = 0; i < 4; i++) {
soma += valores[i];
}
5. Limites de Arrays e Comportamento Indefinido
Um dos aspectos mais críticos em C é que não há verificação automática de limites. O compilador confia no programador. Acessar uma posição além do tamanho declarado é um erro clássico:
int arr[5] = {1,2,3,4,5};
arr[5] = 100; // ERRO! Índice 5 está fora dos limites (0 a 4)
Isso gera comportamento indefinido (undefined behavior). As consequências podem incluir:
- Corrupção de memória adjacente (outras variáveis)
- Falhas de segmentação (segmentation fault)
- Resultados imprevisíveis e difíceis de depurar
Importante: o compilador não emite erro de compilação para esse tipo de acesso; o problema só aparece em tempo de execução. Um exemplo clássico de off-by-one:
int dados[3];
for(int i = 0; i <= 3; i++) { // erro: deveria ser i < 3
dados[i] = i * 10;
}
6. Arrays e Ponteiros: Relação Fundamental
Em C, o nome de um array funciona como um ponteiro constante para o primeiro elemento. Isso significa que:
int arr[4] = {10, 20, 30, 40};
int *p = arr; // p aponta para arr[0]
A aritmética de ponteiros é equivalente à indexação:
*(arr + 2) == arr[2] // ambos valem 30
arr[0] == *arr // ambos valem 10
Quando passamos um array para uma função, ele decai para um ponteiro:
void func(int v[]) { ... } // equivalente a void func(int *v) { ... }
Isso tem uma implicação importante: a função perde a informação do tamanho. Por isso, geralmente passamos o tamanho como parâmetro adicional:
void imprimir(int v[], int n) {
for(int i = 0; i < n; i++) {
printf("%d ", v[i]);
}
}
7. Boas Práticas para Evitar Violação de Limites
Para evitar os perigos do acesso fora dos limites, adote estas práticas:
Usar constantes simbólicas com #define:
#define TAM 10
int vetor[TAM];
for(int i = 0; i < TAM; i++) { ... }
Verificar índices manualmente em loops:
for(int i = 0; i < tamanho_atual; i++) { ... } // sempre <, nunca <=
Para strings, usar funções seguras:
char destino[10];
snprintf(destino, sizeof(destino), "%s", origem); // limita o tamanho
strncpy(destino, origem, sizeof(destino) - 1); // cópia segura
Ferramentas de análise:
- Valgrind (Linux): detecta acessos inválidos em tempo de execução
- AddressSanitizer (GCC/Clang): compilar com -fsanitize=address
- Static analyzers: cppcheck, clang-tidy para detectar problemas em tempo de compilação
8. Exemplos Práticos e Erros Comuns
Exemplo 1: Cálculo de média com array de notas
#include <stdio.h>
int main() {
float notas[5] = {7.5, 8.0, 6.5, 9.0, 5.5};
float soma = 0.0;
for(int i = 0; i < 5; i++) {
soma += notas[i];
}
float media = soma / 5;
printf("Média: %.2f\n", media);
return 0;
}
Exemplo 2: Cópia segura com verificação de limites
#include <stdio.h>
void copiar_array(int destino[], int origem[], int n) {
for(int i = 0; i < n; i++) {
destino[i] = origem[i];
}
}
int main() {
int fonte[3] = {10, 20, 30};
int alvo[3];
copiar_array(alvo, fonte, 3); // seguro: passamos o tamanho
for(int i = 0; i < 3; i++) {
printf("%d ", alvo[i]);
}
return 0;
}
Exemplo 3: Erro clássico de off-by-one
#include <stdio.h>
int main() {
int arr[3];
// ERRO: i <= 3 acessa arr[3], que está fora dos limites
for(int i = 0; i <= 3; i++) {
arr[i] = i * 10; // comportamento indefinido em i=3
}
return 0;
}
Exemplo 4: Acesso inválido em matriz multidimensional
#include <stdio.h>
int main() {
int mat[2][3] = {{1,2,3},{4,5,6}};
// ERRO: acessando coluna 3, que não existe (índices válidos: 0,1,2)
mat[1][3] = 99; // comportamento indefinido
printf("%d\n", mat[1][3]); // imprevisível
return 0;
}
Referências
- cppreference.com - Array declaration — Documentação oficial sobre declaração de arrays em C, incluindo sintaxe e regras de inicialização.
- GNU C Manual - Arrays — Capítulo completo do manual GNU sobre arrays, com exemplos práticos e explicações detalhadas.
- Valgrind User Manual — Documentação oficial do Valgrind, ferramenta essencial para detectar acessos inválidos a memória em programas C.
- SEI CERT C Coding Standard - ARR30-C — Padrão de codificação segura da SEI CERT sobre prevenção de acesso fora dos limites em arrays.
- Beej's Guide to C - Arrays and Pointers — Tutorial prático e acessível sobre a relação entre arrays e ponteiros, com exemplos de aritmética de ponteiros.