O que são, como declarar, acessar — e por que a falta de verificação de limites é perigosa
Imagine que você precisa guardar as notas de 5 alunos. Sem array, seria:
float nota0, nota1, nota2, nota3, nota4; // 5 variáveis separadas — impraticável para 100 alunos
Com array:
float notas[5]; // 5 floats em sequência na memória, acessíveis por índice
| Índice | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| Variável | notas[0] |
notas[1] |
notas[2] |
notas[3] |
notas[4] |
| Endereço (exemplo) | 1000 | 1004 | 1008 | 1012 | 1016 |
Cada float ocupa 4 bytes, então os endereços avançam de 4 em 4. Os elementos ficam lado a lado na memória — isso é o que torna o acesso por índice tão rápido.
tipo nome[tamanho];
O tamanho deve ser uma constante inteira positiva conhecida em tempo de compilação (C89/C90). Em C99 em diante, pode ser uma variável (VLA — Variable Length Array), mas isso é assunto avançado.
// Forma 1 — inicialização na declaração com lista int idades[5] = {20, 25, 30, 35, 40}; // Forma 2 — tamanho deduzido pelo compilador (conta os elementos) int idades[] = {20, 25, 30, 35, 40}; // compilador define tamanho = 5 // Forma 3 — inicialização parcial (restante recebe 0) int idades[5] = {20, 25}; // idades = {20, 25, 0, 0, 0} // Forma 4 — zerar todo o array (idioma C clássico) int idades[5] = {0}; // idades = {0, 0, 0, 0, 0} // Forma 5 — declaração sem inicialização (contém lixo de memória!) int idades[5]; // PERIGO: valores imprevisíveis
= {0} é a forma mais compacta de zerar um array inteiro. Funciona porque C garante que elementos não especificados na lista de inicialização recebem zero. Assim, especificar apenas o primeiro como zero faz os demais seguirem a mesma regra.
#include <stdio.h>
int main() {
int notas[5] = {7, 8, 6, 9, 5};
notas[2] = 10; // modifica o 3º elemento (índice 2)
printf("Nota 3: %d\n", notas[2]); // lê o 3º elemento
return 0;
}
#include <stdio.h>
int main() {
float notas[] = {7.5, 8.0, 6.5, 9.0, 5.5};
int n = 5;
float soma = 0;
for (int i = 0; i < n; i++) {
soma += notas[i];
printf("notas[%d] = %.1f\n", i, notas[i]);
}
printf("Média: %.2f\n", soma / n);
return 0;
}
#include <stdio.h>
#define N 5
int main() {
int v[N];
printf("Digite %d inteiros:\n", N);
for (int i = 0; i < N; i++) {
printf("v[%d]: ", i);
scanf("%d", &v[i]); // &v[i] = endereço do elemento i
}
printf("Você digitou: ");
for (int i = 0; i < N; i++)
printf("%d ", v[i]);
printf("\n");
return 0;
}
ArrayIndexOutOfBoundsException, interrompendo o programa de forma controlada. Em C, não existe nenhuma verificação automática de limites. Acessar além do fim do array é comportamento indefinido (Undefined Behavior — UB).
int[] v = new int[5];
v[10] = 42; // lança exceção
✅ Programa para, erro identificado.
int v[5];
v[10] = 42; // UB — sem aviso!
printf("%d\n", v[10]);
❌ Pode parecer OK e ainda assim ser catastrófico.
Quando você escreve em v[10] num array de tamanho 5, você está escrevendo em memória que pertence a outra variável, ao sistema operacional, ou a código do programa. Os efeitos são imprevisíveis:
| Cenário | O que pode acontecer |
|---|---|
| Leitura fora do array | Retorna "lixo" — valor de outra variável ou área de memória não inicializada |
| Escrita fora do array | Corrompe silenciosamente outra variável; o bug pode se manifestar muito depois, em ponto diferente do código |
| Acesso a área do SO | Segmentation fault — programa termina abruptamente |
| Buffer overflow | Vulnerabilidade de segurança clássica — pode ser explorada por hackers para executar código arbitrário |
i < n) e nunca i <= n.
int v[5] = {1, 2, 3, 4, 5};
// ERRADO — acessa v[5] que não existe!
for (int i = 0; i <= 5; i++) // ← i <= 5 (deveria ser i < 5)
printf("%d ", v[i]);
// CORRETO
for (int i = 0; i < 5; i++) // ← i < 5
printf("%d ", v[i]);
A diferença entre < e <= na condição do loop é responsável por inúmeros bugs em C. Um array de tamanho n tem índices de 0 a n-1. O índice n já está fora.
Questões sobre os conceitos desta parte. A Parte 2 traz mais 4 questões sobre sizeof, matrizes e C vs Java.