Arrays dinâmicos
Nesta aula, você aprenderá a criar e manipular arrays dinâmicos em C, alocando memória na heap com malloc, calloc e realloc. Verá como redimensionar arrays, construir matrizes dinâmicas e liberar corretamente a memória para evitar vazamentos.
Até agora, trabalhamos com arrays de tamanho fixo, definidos em tempo de compilação. Em muitos problemas reais, porém, o tamanho dos dados só é conhecido em tempo de execução, ou precisa crescer e encolher dinamicamente. Para isso, a linguagem C oferece funções de alocação dinâmica de memória (malloc, calloc, realloc) que permitem criar arrays na heap, gerenciando a memória manualmente. Nesta aula, vamos explorar como alocar, redimensionar e liberar arrays dinâmicos, incluindo matrizes (arrays de arrays).
Alocando arrays na heap
Em C, arrays estáticos são alocados na pilha (stack) e têm tamanho fixo. Para criar um array cujo tamanho é definido em tempo de execução, usamos a função malloc (memory allocation), que reserva um bloco de bytes na heap e retorna um ponteiro para o início desse bloco. A sintaxe típica é:
int *arr = (int*) malloc(n * sizeof(int));
Aqui, n é o número de elementos desejado. sizeof(int) dá o tamanho de cada elemento em bytes. O cast (int*) é opcional em C, mas recomendado para clareza (em C++ é obrigatório). Se a alocação falhar (por falta de memória), malloc retorna NULL. Sempre verifique o retorno!
Outra função útil é calloc, que aloca e inicializa todos os bytes com zero:
int *arr = (int*) calloc(n, sizeof(int));
Exemplo prático de alocação de um array de inteiros:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Quantos elementos? ");
scanf("%d", &n);
int *arr = (int*) malloc(n * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "Erro de alocação\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}
Redimensionando
Muitas vezes precisamos aumentar ou diminuir o tamanho de um array dinâmico. A função realloc (reallocation) permite redimensionar um bloco de memória previamente alocado. Ela recebe o ponteiro original e o novo tamanho em bytes, e retorna um ponteiro para o bloco redimensionado (que pode estar em um novo endereço).
int *arr = (int*) malloc(5 * sizeof(int));
// ... usar arr ...
arr = (int*) realloc(arr, 10 * sizeof(int)); // aumenta para 10 elementos
Cuidado: realloc pode mover o bloco para outra posição, copiando o conteúdo antigo. O ponteiro original não deve mais ser usado; sempre atribua o retorno de realloc ao mesmo ponteiro (ou a um temporário para evitar vazamento se falhar). Exemplo seguro:
int *temp = (int*) realloc(arr, novo_tamanho * sizeof(int));
if (temp == NULL) {
// erro: arr ainda existe, mas não foi redimensionado
fprintf(stderr, "Falha ao redimensionar\n");
} else {
arr = temp;
}
Exemplo completo de redimensionamento dinâmico (como um vetor que cresce conforme necessário):
#include <stdio.h>
#include <stdlib.h>
int main() {
int capacidade = 2;
int *arr = (int*) malloc(capacidade * sizeof(int));
int count = 0;
int valor;
printf("Digite números (negativo para parar):\n");
while (1) {
scanf("%d", &valor);
if (valor < 0) break;
if (count == capacidade) {
capacidade *= 2;
int *temp = (int*) realloc(arr, capacidade * sizeof(int));
if (temp == NULL) {
fprintf(stderr, "Erro ao realocar\n");
free(arr);
return 1;
}
arr = temp;
}
arr[count++] = valor;
}
printf("Valores lidos: ");
for (int i = 0; i < count; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}
Matrizes dinâmicas
Uma matriz dinâmica é um array de ponteiros, onde cada ponteiro aponta para um array (linha). Podemos alocar uma matriz de linhas x colunas da seguinte forma:
int **mat = (int**) malloc(linhas * sizeof(int*));
for (int i = 0; i < linhas; i++) {
mat[i] = (int*) malloc(colunas * sizeof(int));
}
Isso cria uma matriz onde cada linha pode ter tamanho diferente (matriz irregular ou jagged array). Para acessar o elemento [i][j], usamos mat[i][j] normalmente.
Exemplo completo de criação e preenchimento de uma matriz 3x4:
#include <stdio.h>
#include <stdlib.h>
int main() {
int linhas = 3, colunas = 4;
int **mat = (int**) malloc(linhas * sizeof(int*));
for (int i = 0; i < linhas; i++) {
mat[i] = (int*) malloc(colunas * sizeof(int));
}
// Preenche com valores i+j
for (int i = 0; i < linhas; i++) {
for (int j = 0; j < colunas; j++) {
mat[i][j] = i + j;
}
}
// Imprime
for (int i = 0; i < linhas; i++) {
for (int j = 0; j < colunas; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
// Libera memória (importante!)
for (int i = 0; i < linhas; i++) {
free(mat[i]);
}
free(mat);
return 0;
}
Liberando
Toda memória alocada dinamicamente com malloc, calloc ou realloc deve ser liberada com free quando não for mais necessária. Caso contrário, ocorre vazamento de memória (memory leak). A sintaxe é simples:
free(ponteiro);
Regras importantes:
- Nunca libere o mesmo ponteiro duas vezes (undefined behavior).
- Após liberar, o ponteiro se torna inválido; atribua
NULLa ele para evitar uso acidental. - Para matrizes dinâmicas, libere primeiro as linhas (cada
mat[i]) e depois o array de ponteiros (mat). - Sempre libere na ordem inversa da alocação.
Exemplo de função segura para liberar matriz:
void free_matrix(int **mat, int linhas) {
if (mat == NULL) return;
for (int i = 0; i < linhas; i++) {
free(mat[i]);
}
free(mat);
}
Boas práticas e observações finais
- Sempre verifique o retorno de
malloc,callocerealloc. - Evite vazamentos: para cada
mallocdeve haver umfree. - Use
callocse precisar de inicialização com zero. - Documente seu código: indique quem é responsável por liberar a memória.
- Considere usar estruturas que encapsulam o array dinâmico (como um vetor em C++) para gerenciar automaticamente.
Referências
- malloc - cppreference.com
- calloc - cppreference.com
- realloc - cppreference.com
- free - cppreference.com
- GNU C Library: Memory Allocation
- Wikilivros: Alocação dinâmica em C
Exercícios
-
Escreva um programa que leia um número inteiro
ndo usuário, aloque dinamicamente um array denfloats, preencha com valores lidos do teclado e calcule a média. Não esqueça de liberar a memória.✓ Resposta:#include <stdio.h> #include <stdlib.h> int main() { int n; printf("Quantos números? "); scanf("%d", &n); float *arr = (float*) malloc(n * sizeof(float)); if (arr == NULL) { printf("Erro de alocação\n"); return 1; } float soma = 0; for (int i = 0; i < n; i++) { scanf("%f", &arr[i]); soma += arr[i]; } printf("Média: %.2f\n", soma / n); free(arr); return 0; } -
Implemente uma função
int* inserir_ordenado(int *arr, int *tam, int valor)que inserevalorem um array ordenado (crescente) e redimensiona o array comrealloc. O parâmetrotamé um ponteiro para o tamanho atual, que deve ser atualizado. Retorne o novo ponteiro (ou NULL em erro).✓ Resposta:#include <stdlib.h> int* inserir_ordenado(int *arr, int *tam, int valor) { int novo_tam = *tam + 1; int *temp = (int*) realloc(arr, novo_tam * sizeof(int)); if (temp == NULL) return NULL; // desloca elementos para abrir espaço int i = *tam - 1; while (i >= 0 && temp[i] > valor) { temp[i + 1] = temp[i]; i--; } temp[i + 1] = valor; *tam = novo_tam; return temp; } -
Crie uma matriz dinâmica de caracteres (strings) para armazenar uma lista de nomes. O programa deve ler um número
nde nomes e depois cada nome (máximo 99 caracteres). Armazene cada nome em uma linha alocada dinamicamente. Ao final, imprima os nomes e libere toda a memória.✓ Resposta:#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int n; printf("Quantos nomes? "); scanf("%d", &n); getchar(); // limpa o newline char **nomes = (char**) malloc(n * sizeof(char*)); if (nomes == NULL) return 1; char buffer[100]; for (int i = 0; i < n; i++) { printf("Nome %d: ", i+1); fgets(buffer, 100, stdin); buffer[strcspn(buffer, "\n")] = 0; // remove newline nomes[i] = (char*) malloc((strlen(buffer)+1) * sizeof(char)); if (nomes[i] == NULL) { /* tratar erro */ } strcpy(nomes[i], buffer); } printf("\nNomes lidos:\n"); for (int i = 0; i < n; i++) { printf("%s\n", nomes[i]); } for (int i = 0; i < n; i++) { free(nomes[i]); } free(nomes); return 0; } -
Escreva um programa que leia uma sequência de números inteiros até que o usuário digite 0, armazenando-os em um array dinâmico que dobra de capacidade quando necessário. Ao final, exiba os números na ordem inversa. Use
reallocpara redimensionar.✓ Resposta:#include <stdio.h> #include <stdlib.h> int main() { int cap = 2; int *arr = (int*) malloc(cap * sizeof(int)); if (arr == NULL) return 1; int count = 0; int valor; printf("Digite números (0 para parar):\n"); while (1) { scanf("%d", &valor); if (valor == 0) break; if (count == cap) { cap *= 2; int *temp = (int*) realloc(arr, cap * sizeof(int)); if (temp == NULL) { free(arr); return 1; } arr = temp; } arr[count++] = valor; } printf("Ordem inversa: "); for (int i = count - 1; i >= 0; i--) { printf("%d ", arr[i]); } printf("\n"); free(arr); return 0; } -
Implemente uma função que receba uma matriz dinâmica de inteiros (int **mat), o número de linhas e colunas, e retorne uma nova matriz (alocada dinamicamente) que seja a transposta da original. A função deve ter o protótipo:
int** transpor(int **mat, int linhas, int colunas). Teste com uma matriz 2x3.✓ Resposta:#include <stdlib.h> int** transpor(int **mat, int linhas, int colunas) { int **t = (int**) malloc(colunas * sizeof(int*)); if (t == NULL) return NULL; for (int i = 0; i < colunas; i++) { t[i] = (int*) malloc(linhas * sizeof(int)); if (t[i] == NULL) { for (int j = 0; j < i; j++) free(t[j]); free(t); return NULL; } } for (int i = 0; i < linhas; i++) { for (int j = 0; j < colunas; j++) { t[j][i] = mat[i][j]; } } return t; } // Exemplo de uso: #include <stdio.h> int main() { int l=2, c=3; int **m = (int**) malloc(l * sizeof(int*)); for (int i=0; i<l; i++) m[i] = (int*) malloc(c * sizeof(int)); // preenche for (int i=0; i<l; i++) for (int j=0; j<c; j++) m[i][j] = i*c + j; int **t = transpor(m, l, c); if (t) { for (int i=0; i<c; i++) { for (int j=0; j<l; j++) printf("%d ", t[i][j]); printf("\n"); } // libera t for (int i=0; i<c; i++) free(t[i]); free(t); } // libera m for (int i=0; i<l; i++) free(m[i]); free(m); return 0; }