Strings em C
Nesta aula, exploramos strings em C como arrays de caracteres terminados pelo caractere nulo '\0'. Aprendemos a inicializar strings, a importância do terminador nulo e cuidados essenciais para evitar erros comuns como estouro de buffer.
Em C, strings não são um tipo de dado nativo como em linguagens de alto nível. Em vez disso, são representadas como arrays de caracteres (char[]) que terminam com um caractere especial: o terminador nulo ('\0'). Essa abordagem exige que o programador gerencie manualmente o espaço e o término da string, o que pode levar a erros se não for feito com cuidado.
Nesta aula, vamos explorar como declarar, inicializar e manipular strings em C, destacando a importância do terminador nulo e os cuidados necessários para evitar problemas como estouro de buffer e acesso inválido à memória.
Arrays de char
Uma string em C é armazenada como um array de caracteres do tipo char. Cada caractere ocupa um byte na memória. O array pode ser declarado com um tamanho fixo, e a string deve caber dentro desse espaço, incluindo o terminador nulo. Por exemplo, char nome[20]; declara um array que pode armazenar até 19 caracteres úteis mais o '\0'.
Os caracteres individuais podem ser acessados usando índices, começando em 0. É importante lembrar que o array é apenas um bloco de memória contíguo; não há verificação de limites em tempo de execução. Portanto, é responsabilidade do programador garantir que não se ultrapasse o tamanho declarado.
Exemplo de declaração e acesso:
#include <stdio.h>
int main() {
char str[6] = {'O', 'l', 'a', '!', '\0'}; // array de 6 chars, mas usamos 5
printf("%s\n", str);
printf("Primeiro char: %c\n", str[0]);
return 0;
}Terminador nulo (\0)
O terminador nulo ('\0') é um caractere especial com valor ASCII 0 que marca o fim da string. Todas as funções da biblioteca padrão de C (como strlen, strcpy, printf com formato %s) dependem desse caractere para saber onde a string termina. Se ele estiver ausente, o comportamento é indefinido, podendo causar a leitura de memória além do array.
É crucial sempre reservar espaço para o terminador nulo ao declarar um array. Por exemplo, para armazenar a palavra "Casa" (4 letras), o array deve ter pelo menos 5 elementos: 4 para as letras e 1 para o '\0'.
Exemplo que demonstra a importância do terminador:
#include <stdio.h>
#include <string.h>
int main() {
char sem_terminador[4] = {'T', 'e', 's', 't'}; // sem \0
char com_terminador[5] = {'T', 'e', 's', 't', '\0'};
printf("Sem terminador: %s\n", sem_terminador); // perigoso!
printf("Com terminador: %s\n", com_terminador);
// strlen pode retornar valor incorreto sem terminador
printf("Tamanho (sem): %lu\n", strlen(sem_terminador));
printf("Tamanho (com): %lu\n", strlen(com_terminador));
return 0;
}Inicialização
Existem várias formas de inicializar uma string em C. A mais comum é usando uma string literal entre aspas duplas, que já inclui o terminador nulo automaticamente. Exemplo: char str[] = "Hello"; cria um array de 6 caracteres (5 letras + '\0').
Outra forma é inicializar explicitamente cada caractere, como vimos anteriormente. Nesse caso, é necessário incluir manualmente o '\0'. Também é possível declarar um array de tamanho fixo e depois copiar uma string para ele usando strcpy, mas é preciso garantir que o tamanho seja suficiente.
Exemplos de inicialização:
#include <stdio.h>
#include <string.h>
int main() {
// Inicialização com literal
char s1[] = "C\n";
// Inicialização explícita (com \0)
char s2[4] = {'C', '\n', '!', '\0'};
// Inicialização parcial: os elementos não especificados são zero (que é '\0')
char s3[10] = "Oi"; // s3[0]='O', s3[1]='i', s3[2]='\0', resto zero
printf("%s%s%s", s1, s2, s3);
return 0;
}Cuidados
Trabalhar com strings em C exige atenção redobrada para evitar erros comuns:
- Estouro de buffer: ocorre quando se tenta armazenar mais caracteres do que o espaço reservado, incluindo o terminador. Funções como
strcpyescanfcom%snão verificam limites. Usestrncpyousnprintfpara limitar o número de caracteres. - Falta do terminador nulo: se o
'\0'não for colocado, funções comoprintfestrlenpodem ler memória além do array, causando comportamento imprevisível. - Tamanho insuficiente: ao declarar um array, lembre-se de incluir espaço para o terminador. Exemplo: para armazenar "abc", o array deve ter pelo menos 4 elementos.
- Comparação de strings: não use o operador
==para comparar strings, pois ele compara endereços de memória, não o conteúdo. Usestrcmp. - Atribuição: não é possível atribuir uma string a um array após a declaração com
=(exceto na inicialização). Usestrcpyoumemcpy.
Exemplo de cuidados:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[5];
// strcpy(buffer, "Hello!"); // estouro de buffer! (6 caracteres + \0)
strncpy(buffer, "Hello!", sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = '\0'; // garante terminação
printf("%s\n", buffer);
// Comparação correta
char str1[] = "abc";
char str2[] = "abc";
if (strcmp(str1, str2) == 0) {
printf("Iguais\n");
}
return 0;
}Boas práticas
Sempre inicialize strings com um valor conhecido, especialmente se forem globais ou estáticas. Use funções seguras como strncpy e snprintf em vez de suas versões sem limite. Considere usar a biblioteca string.h para manipulação segura. Ao ler entrada do usuário, evite scanf("%s") sem especificar largura; prefira fgets.
Exemplo de leitura segura:
#include <stdio.h>
int main() {
char nome[50];
printf("Digite seu nome: ");
fgets(nome, sizeof(nome), stdin);
// remove o \n se presente
size_t len = strlen(nome);
if (len > 0 && nome[len-1] == '\n') nome[len-1] = '\0';
printf("Olá, %s!\n", nome);
return 0;
}Referências
- cppreference: String literals
- cppreference: Byte string functions
- GNU C Library: Strings
- TutorialsPoint: C Strings
- learn-c.org: Strings
Exercícios
Crie um programa que declare uma string com seu nome e a imprima caractere por caractere (incluindo o terminador nulo) usando um loop.
✓ Resposta:#include <stdio.h> int main() { char nome[] = "João"; for(int i = 0; i <= sizeof(nome)/sizeof(nome[0]); i++) { if(nome[i] == '\0') printf("\\0\n"); else printf("%c\n", nome[i]); } return 0; }Explique por que o código abaixo está errado e corrija-o:
char str[3] = "abc"; printf("%s", str);✓ Resposta: O array tem tamanho 3, mas a string "abc" precisa de 4 caracteres (a, b, c, \0). Isso causa estouro de buffer. Corrigido:char str[4] = "abc";ouchar str[] = "abc";Escreva uma função que receba uma string e retorne seu comprimento sem usar
strlen.✓ Resposta:int meu_strlen(const char *s) { int len = 0; while(s[len] != '\0') len++; return len; }O que o seguinte trecho imprime?
char s[] = {'a','b','c'}; printf("%s", s);Justifique.✓ Resposta: O comportamento é indefinido porque o array não possui terminador nulo. Oprintfcontinuará lendo memória até encontrar um '\0' acidental, podendo imprimir lixo ou causar falha de segmentação.Usando
fgets, leia uma linha do usuário e imprima seu comprimento (sem o\n).✓ Resposta:#include <stdio.h> #include <string.h> int main() { char linha[100]; printf("Digite algo: "); fgets(linha, sizeof(linha), stdin); linha[strcspn(linha, "\n")] = '\0'; // remove \n printf("Comprimento: %lu\n", strlen(linha)); return 0; }