Strings em C
Esta aula aborda strings em C, que são arrays de caracteres terminados pelo caractere nulo '\0'. Você aprenderá como declarar, inicializar e manipular strings, além dos cuidados necessários 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, uma string é representada como um array de caracteres (char) que termina com um caractere especial chamado terminador nulo ('\0'). Este modelo oferece controle total sobre a memória, mas exige atenção redobrada para evitar erros.
Nesta aula, exploraremos como declarar, inicializar e manipular strings, destacando os cuidados essenciais para escrever código seguro e eficiente.
Arrays de char
Uma string em C é essencialmente um array unidimensional de caracteres (char). Cada caractere ocupa um byte e é armazenado em posições consecutivas de memória. O final da string é marcado pelo caractere nulo ('\0'), que tem valor ASCII 0. Por exemplo, a string "Olá" é armazenada como:
char str[4] = {'O', 'l', 'á', '\0'};O array deve ter espaço suficiente para todos os caracteres da string mais o terminador nulo. Se você declarar char nome[10], pode armazenar até 9 caracteres úteis, pois o 10º será o '\0'. É importante lembrar que strings em C são mutáveis (os caracteres individuais podem ser alterados), ao contrário de strings literais que são constantes.
Ao acessar um caractere, usamos índice como em qualquer array: str[0] é o primeiro caractere. A função strlen() da biblioteca <string.h> retorna o comprimento da string sem contar o terminador nulo.
Terminador nulo (\0)
O terminador nulo é um caractere com código ASCII 0, representado pela sequência de escape '\0'. Ele é crucial porque todas as funções de manipulação de strings (como strcpy, strcat, printf com %s) dependem dele para saber onde a string termina. Sem o terminador nulo, essas funções continuariam lendo memória além do array, causando comportamento indefinido e potenciais falhas de segurança.
Quando você declara uma string literal como "Hello", o compilador automaticamente adiciona o '\0' ao final. Por exemplo, "Hello" ocupa 6 bytes: 'H','e','l','l','o','\0'. Ao criar um array manualmente, você deve garantir que o terminador esteja presente.
Exemplo de perigo: se você copiar uma string para um array que não tem espaço para o '\0', a string resultante não será terminada corretamente.
char destino[5];
strcpy(destino, "Hello"); // ERRO! "Hello" precisa de 6 bytes (5 letras + '\0')Isso causa estouro de buffer, um grave problema de segurança.
Inicialização
Existem várias formas de inicializar uma string em C:
1. Inicialização com string literal:
char str1[] = "Hello"; // tamanho automático: 6
char str2[6] = "Hello"; // tamanho explícito
char str3[10] = "Hello"; // espaço extra preenchido com zeros2. Inicialização como array de caracteres:
char str4[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // necessário incluir '\0'3. Inicialização parcial (restante é zero):
char str5[10] = {'H', 'e', 'l'}; // posições 0,1,2 definidas; o resto é zero ('\0')É importante notar que ao usar a sintaxe com chaves, você deve incluir explicitamente o '\0' se quiser que a string seja terminada. Caso contrário, o array não será uma string válida.
Também é possível usar ponteiros para strings literais, mas isso cria uma string constante (imutável) e o ponteiro aponta para uma área de memória somente leitura:
char *str6 = "Hello"; // string literal constante
// str6[0] = 'h'; // ERRO! comportamento indefinido (tentativa de modificar constante)Cuidados
Trabalhar com strings em C exige atenção a diversos detalhes para evitar erros:
- Estouro de buffer: Sempre garanta que o array de destino tenha tamanho suficiente para armazenar a string incluindo o
'\0'. Use funções seguras comostrncpyousnprintfquando possível. - Terminador nulo: Nunca esqueça de incluir o
'\0'ao construir strings manualmente. Funções comostrcatestrcpyesperam que a origem tenha terminador. - Strings constantes: Não tente modificar o conteúdo de uma string literal através de um ponteiro. Isso causa comportamento indefinido.
- Funções inseguras: Evite usar
gets()(removido do padrão C11) e prefirafgets()para ler strings do teclado. - Comparação: Não use
==para comparar strings; usestrcmp(). - Retorno de strings de funções: Cuidado ao retornar ponteiros para arrays locais (que deixam de existir após a função). Prefira alocar memória dinamicamente ou passar um buffer como parâmetro.
Exemplo de código que ilustra alguns cuidados:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[20];
printf("Digite seu nome: ");
fgets(buffer, sizeof(buffer), stdin); // seguro: limita o tamanho
// Remove o newline se presente
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
}
printf("Olá, %s!\n", buffer);
return 0;
}Boas Práticas
Sempre inicialize arrays de caracteres com \0 para evitar lixo de memória. Use constantes para definir tamanhos máximos. Prefira funções que limitam o número de caracteres copiados, como strncpy (mas lembre-se de que ela não adiciona o terminador nulo se o limite for atingido).
Referências
- cppreference: Strings em C
- GNU C Library: String e Array Utilities
- Wikipedia: C string handling
- Wikipedia: String
- TutorialsPoint: C Strings
Exercícios
Escreva 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[] = "Maria"; for (int i = 0; i <= sizeof(nome)/sizeof(nome[0]) - 1; i++) { printf("Caractere %d: '%c' (ASCII %d)\n", i, nome[i], nome[i]); } return 0; }O que acontece se você declarar
char str[5] = "Hello";? Explique.✓ Resposta: Isso é um erro de compilação porque a string literal "Hello" requer 6 bytes (5 letras + '\0'), mas o array tem apenas 5. O compilador rejeitará ou emitirá um aviso, e o comportamento é indefinido. Em alguns compiladores, pode compilar, mas o terminador nulo não será armazenado, causando problemas.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; }Qual a diferença entre
char s[] = "abc";echar *s = "abc";?✓ Resposta: A primeira declara um array mutável de 4 caracteres (incluindo '\0') na pilha. A segunda declara um ponteiro para uma string literal constante, que geralmente fica em uma área de memória somente leitura. Tentar modificar o conteúdo através do ponteiro causa comportamento indefinido. O array pode ser modificado, o ponteiro não.Escreva um programa que leia uma linha de texto (até 100 caracteres) e a imprima de trás para frente.
✓ Resposta:#include <stdio.h> #include <string.h> int main() { char linha[101]; printf("Digite uma linha: "); fgets(linha, sizeof(linha), stdin); // Remove newline size_t len = strlen(linha); if (len > 0 && linha[len-1] == '\n') { linha[len-1] = '\0'; len--; } for (int i = len - 1; i >= 0; i--) { putchar(linha[i]); } putchar('\n'); return 0; }