Arrays de ponteiros
Nesta aula, você aprenderá a trabalhar com arrays de ponteiros em C, com foco em arrays de strings (char*[]) e na manipulação de argumentos da linha de comando via argv. Vamos explorar a iteração sobre esses arrays, casos de uso práticos e boas práticas, com exemplos de código e exercícios para fixação.
Arrays de ponteiros são uma das construções mais versáteis e poderosas da linguagem C. Eles permitem armazenar e manipular coleções de dados de forma flexível, especialmente quando os elementos têm tamanho variável, como strings. Nesta aula, vamos aprofundar o conceito de arrays de ponteiros, com foco em char*[] (arrays de strings), na interface argv do main, na iteração sobre esses arrays e nos principais casos de uso no dia a dia do programador C.
Dominar arrays de ponteiros é essencial para entender como C lida com memória e ponteiros em um nível mais alto. Veremos como declarar, inicializar, percorrer e utilizar essas estruturas, além de discutir boas práticas para evitar erros comuns como vazamentos de memória e acesso indevido.
Array de strings (char*[])
Em C, uma string é um array de caracteres terminado em '\0'. Um array de strings é, portanto, um array de ponteiros para caracteres (char*). Cada elemento do array aponta para o primeiro caractere de uma string. Essa estrutura é extremamente útil para armazenar listas de nomes, mensagens, opções de configuração, etc.
A declaração típica é:
char *nomes[] = {"Ana", "Bruno", "Carla", "Diego"};Aqui, nomes é um array de 4 ponteiros, cada um apontando para uma string literal. É importante notar que as strings literais são constantes, então tentar modificá-las pode causar um comportamento indefinido. Para strings modificáveis, usamos arrays de caracteres, mas para listas de strings que não serão alteradas, o padrão acima é eficiente e conciso.
Outra forma de declarar é sem inicialização, para preencher dinamicamente:
char *frutas[3];
frutas[0] = "Maçã";
frutas[1] = "Banana";
frutas[2] = "Laranja";O tamanho do array deve ser conhecido em tempo de compilação, a menos que usemos alocação dinâmica (com malloc). Veremos isso mais adiante.
argv
A função main em C pode receber argumentos da linha de comando. A assinatura padrão é:
int main(int argc, char *argv[])Onde argc é o número de argumentos (incluindo o nome do programa) e argv é um array de ponteiros para strings, cada uma contendo um argumento. argv[0] é o nome do programa, argv[1] é o primeiro argumento, e assim por diante. O último elemento é NULL (embora a convenção seja usar argc para saber o tamanho).
Exemplo de uso:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Número de argumentos: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}Se compilarmos e executarmos ./programa teste 123, a saída será:
Número de argumentos: 3
argv[0] = ./programa
argv[1] = teste
argv[2] = 123É comum usar argv para passar opções e parâmetros a um programa, como flags (-v, -o arquivo). A análise desses argumentos geralmente é feita com loops e funções como strcmp.
Iteração
Iterar sobre um array de ponteiros é simples: usamos um laço for ou while percorrendo os índices, ou um ponteiro para o início do array, avançando até encontrar NULL (se o array for terminado por NULL).
Exemplo com índice:
char *cores[] = {"Vermelho", "Verde", "Azul", NULL};
for (int i = 0; cores[i] != NULL; i++) {
printf("%s\n", cores[i]);
}Exemplo com ponteiro:
char **p = cores;
while (*p != NULL) {
printf("%s\n", *p);
p++;
}No caso de argv, podemos iterar até i < argc ou até argv[i] != NULL (mas o padrão é usar argc).
Iterar com ponteiro é eficiente e mostra a relação entre arrays e ponteiros em C.
Casos de uso
Arrays de ponteiros são usados em muitas situações práticas:
- Listas de strings constantes: como nomes de meses, dias da semana, mensagens de erro.
- Argumentos da linha de comando: já vimos
argv. - Tabelas de lookup: por exemplo, uma lista de nomes e seus códigos.
- Manipulação de múltiplas strings: ordenar, filtrar, etc.
- Implementação de estruturas de dados: como um array de ponteiros para funções (embora não seja string).
Um exemplo prático: um programa que imprime os dias da semana:
#include <stdio.h>
int main() {
char *dias[] = {"Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"};
int n = sizeof(dias) / sizeof(dias[0]);
for (int i = 0; i < n; i++) {
printf("Dia %d: %s\n", i+1, dias[i]);
}
return 0;
}Outro caso: um mini interpretador de comandos que compara a entrada com uma lista:
#include <stdio.h>
#include <string.h>
int main() {
char *comandos[] = {"help", "quit", "version"};
char entrada[20];
printf("Digite um comando: ");
scanf("%s", entrada);
for (int i = 0; i < 3; i++) {
if (strcmp(entrada, comandos[i]) == 0) {
printf("Comando reconhecido: %s\n", comandos[i]);
return 0;
}
}
printf("Comando desconhecido!\n");
return 1;
}Boas práticas e observações finais
Ao trabalhar com arrays de ponteiros, lembre-se de:
- Não modificar strings literais (elas são armazenadas em região somente leitura).
- Garantir que o array tenha um tamanho definido ou um terminador NULL para evitar estouros.
- Quando alocar dinamicamente, liberar a memória com
free. - Preferir
const char *quando as strings não serão alteradas, para melhor documentação e segurança. - Usar
sizeof(arr)/sizeof(arr[0])para obter o número de elementos quando o tamanho é conhecido em tempo de compilação.
Essas práticas ajudam a evitar erros comuns e tornam o código mais robusto.
Referências
- cppreference: Array declaration
- cppreference: Pointer declaration
- GNU C Library: Program Arguments
- GNU C Library: String Arrays
- C Class: Arrays of Pointers
- C Pointer Tutorial (Yale)
Exercícios
- Escreva um programa que declare um array de ponteiros para strings com os nomes de 5 cidades e imprima cada uma em uma linha, usando um loop
for.✓ Resposta:#include <stdio.h> int main() { char *cidades[] = {"São Paulo", "Rio de Janeiro", "Belo Horizonte", "Salvador", "Fortaleza"}; int n = sizeof(cidades) / sizeof(cidades[0]); for (int i = 0; i < n; i++) { printf("%s\n", cidades[i]); } return 0; } - Implemente um programa que receba argumentos pela linha de comando e imprima apenas os argumentos que começam com a letra 'a' (use
argveargc).✓ Resposta:#include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { if (argv[i][0] == 'a') { printf("%s\n", argv[i]); } } return 0; } - Crie um array de ponteiros para strings que represente os meses do ano e imprima o mês correspondente a um número digitado pelo usuário (1-12).✓ Resposta:
#include <stdio.h> int main() { char *meses[] = {"Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"}; int num; printf("Digite um número de 1 a 12: "); scanf("%d", &num); if (num >= 1 && num <= 12) { printf("%s\n", meses[num-1]); } else { printf("Número inválido!\n"); } return 0; } - Escreva uma função que receba um array de ponteiros para strings e o tamanho, e retorne o comprimento total de todas as strings somadas (usando
strlen).✓ Resposta:#include <stdio.h> #include <string.h> int total_length(char *arr[], int n) { int total = 0; for (int i = 0; i < n; i++) { total += strlen(arr[i]); } return total; } int main() { char *palavras[] = {"Casa", "Carro", "Moto"}; int n = sizeof(palavras) / sizeof(palavras[0]); printf("Comprimento total: %d\n", total_length(palavras, n)); return 0; } - Implemente um programa que copie os argumentos da linha de comando para um novo array de ponteiros (alocado dinamicamente) e depois imprima os argumentos em ordem reversa.✓ Resposta:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char **copia = malloc(argc * sizeof(char *)); if (copia == NULL) { fprintf(stderr, "Erro de alocação\n"); return 1; } for (int i = 0; i < argc; i++) { copia[i] = malloc(strlen(argv[i]) + 1); if (copia[i] == NULL) { fprintf(stderr, "Erro de alocação\n"); return 1; } strcpy(copia[i], argv[i]); } // Imprime em ordem reversa for (int i = argc - 1; i >= 0; i--) { printf("%s\n", copia[i]); } // Libera memória for (int i = 0; i < argc; i++) { free(copia[i]); } free(copia); return 0; }