Introdução às Strings
Strings em C são sequências de caracteres utilizadas para representar texto. Tecnicamente, uma string é um array de caracteres terminado por um caractere nulo ('\0'). O acesso aos elementos individuais é feito através de índices, semelhante a vetores.
Declaração de Strings
Existem várias formas de declarar strings em C. A escolha depende se você precisa modificar o conteúdo ou apenas lê-lo.
#include <stdio.h>
int main()
{
// Forma 1: Array de caracteres com inicialização explícita
char texto1[6] = {'o', 'l', 'a', 'm', 'u', 'n'};
// Forma 2: Inicialização com string literal (recomendada)
char texto2[] = "bom dia";
// Forma 3: Ponteiro para string literal (não modificável)
const char *mensagem = "hello world";
// Um caractere individual
char caractere = 'A';
printf("texto2: %s\n", texto2);
printf("mensagem: %s\n", mensagem);
// Modificando elemento do array
texto2[0] = 'B';
printf("texto2 modificado: %s\n", texto2);
return 0;
}
Atenção: Ponterios não inicializados são perigosos. Um ponteiro selvagem (wild pointer) aponta para uma área de memória indefinida, podendo causar erros de segmentação.
Armazenamento de Strings
Toda string em C deve terminar com o caractere nulo '\0'. Este é o marcador de fim de string que funções como printf e strlen utilizam para determinar onde a string termina.
#include <stdio.h>
int main()
{
// Array com término explícito
char dado1[] = {'t', 'e', 's', 't', 'e', '\0'};
// String literal - '\0' é adicionado automaticamente
char dado2[] = "teste";
const char *dado3 = "teste";
int tam1 = sizeof(dado2) / sizeof(dado2[0]);
int tam2 = sizeof(dado3) / sizeof(dado3[0]);
printf("Tamanho de dado2: %d\n", tam1); // 6 (inclui '\0')
printf("Tamanho de dado3 (ponteiro): %d\n", tam2); // 8 (tamanho do ponteiro)
// Sem '\0', a saída pode conter caracteres estranhos
char dado4[] = {'a', 'b', 'c'}; // Sem '\0'
printf("Dado4 sem terminador: %s\n", dado4);
return 0;
}
Diferença entre strlen e sizeof
- strlen: Retorna o número de caracteres válidos (exclui '\0')
- sizeof: Retorna o tamanho total da memória alocada
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "casa";
char buffer[64] = "casa";
const char *ptr = "casa";
printf("strlen(str): %zu\n", strlen(str)); // 4
printf("sizeof(str): %zu\n", sizeof(str)); // 5
printf("strlen(buffer): %zu\n", strlen(buffer)); // 4
printf("sizeof(buffer): %zu\n", sizeof(buffer)); // 64
printf("strlen(ptr): %zu\n", strlen(ptr)); // 4
printf("sizeof(ptr): %zu\n", sizeof(ptr)); // 8 (tamanho do ponteiro)
return 0;
}
Alocação Dinâmica de Memória
Para strings de tamanho variável, utiliza-se alocação dinâmica com malloc, calloc e realloc.
malloc
Aloca memória sem inicializar.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// Alocando 20 bytes
char *buffer = (char *)malloc(20);
if (buffer == NULL) {
printf("Falha na alocação\n");
return 1;
}
// Inicializando com zeros
memset(buffer, 0, 20);
// Copiando string
strcpy(buffer, "programacao");
printf("Buffer: %s\n", buffer);
// Liberando memória
free(buffer);
buffer = NULL;
return 0;
}
calloc
Aloca e inicializa toda a memória com zero.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Alocar espaço para 10 caracteres
char *dados = (char *)calloc(10, sizeof(char));
if (dados == NULL) {
printf("Falha na alocação\n");
return 1;
}
// Memória já inicializada com zeros
printf("Conteúdo inicial: '%s'\n", dados);
free(dados);
return 0;
}
realloc
Redimensiona memória previamente alocada.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *texto = (char *)malloc(16);
if (texto == NULL) return 1;
strcpy(texto, " teste ");
printf("Original: %s\n", texto);
// Redimensionar para 32 bytes
char *novo_texto = (char *)realloc(texto, 32);
if (novo_texto == NULL) {
free(texto);
printf("Falha no realloc\n");
return 1;
}
texto = novo_texto;
strcat(texto, " expandido");
printf("Expandido: %s\n", texto);
free(texto);
return 0;
}
Entrada e Saída de Strings
printf e puts
#include <stdio.h>
int main()
{
const char *mensagem = "Bem-vindo ao programa";
printf("Mensagem: %s\n", mensagem);
puts(mensagem); // puts adiciona quebra de linha
return 0;
}
scanf
#include <stdio.h>
int main()
{
char nome[64];
printf("Digite seu nome: ");
scanf("%s", nome);
printf("Olá, %s!\n", nome);
return 0;
}
gets (obsoleto)
Nota: gets é perigoso e não deve ser usado. Utilize fgets替代.
#include <stdio.h>
int main()
{
char entrada[64];
printf("Digite algo: ");
fgets(entrada, sizeof(entrada), stdin);
printf("Você digitou: %s", entrada);
return 0;
}
Cálculo do Comprimento
#include <stdio.h>
#include <string.h>
int main()
{
char texto[] = "abcd";
char buffer[32] = "xyz";
size_t comp1 = strlen(texto); // 4
size_t comp2 = sizeof(texto); // 5
size_t comp3 = strlen(buffer); // 3
size_t comp4 = sizeof(buffer); // 32
printf("strlen(texto): %zu\n", comp1);
printf("sizeof(texto): %zu\n", comp2);
printf("strlen(buffer): %zu\n", comp3);
printf("sizeof(buffer): %zu\n", comp4);
return 0;
}
Cópia de Strings
strcpy vs strncpy
#include <stdio.h>
#include <string.h>
int main()
{
char destino[32];
const char *origem = "programacao";
// Cópia total
strcpy(destino, origem);
printf("strcpy: %s\n", destino);
// Cópia parcial (5 caracteres)
char destino2[32];
strncpy(destino2, origem, 5);
destino2[5] = '\0'; // Garante término
printf("strncpy: %s\n", destino2);
return 0;
}
Implementação de strcpy
#include <stdio.h>
// Implementação personalizada de strcpy
char *meu_strcpy(char *destino, const char *fonte)
{
if (destino == NULL || fonte == NULL) {
return NULL;
}
char *inicio = destino;
while (*fonte != '\0') {
*destino = *fonte;
destino++;
fonte++;
}
*destino = '\0';
return inicio;
}
int main()
{
char buffer[32];
const char *texto = "exemplo";
meu_strcpy(buffer, texto);
printf("Resultado: %s\n", buffer);
return 0;
}
Concatenação de Strings
strcat
#include <stdio.h>
#include <string.h>
int main()
{
char str1[64] = "Hello";
const char *str2 = " World";
// Concatena str2 ao final de str1
strcat(str1, str2);
printf("Resultado: %s\n", str1); // "Hello World"
return 0;
}
Implementação de strcat
#include <stdio.h>
char *meu_strcat(char *destino, const char *fonte)
{
if (destino == NULL || fonte == NULL) {
return NULL;
}
char *inicio = destino;
// Encontrar o final de destino
while (*destino != '\0') {
destino++;
}
// Copiar fonte para o final
while ((*destino = *fonte) != '\0') {
destino++;
fonte++;
}
return inicio;
}
int main()
{
char buffer[64] = "Primeira ";
const char *adicional = "Segunda parte";
meu_strcat(buffer, adicional);
printf("Concatenado: %s\n", buffer);
return 0;
}
Comparação de Strings
strcmp vs strncmp
#include <stdio.h>
#include <string.h>
int main()
{
const char *s1 = "abcd";
const char *s2 = "abce";
// Comparação completa
int resultado = strcmp(s1, s2);
if (resultado == 0) {
printf("Strings iguais\n");
} else if (resultado < 0) {
printf("s1 < s2\n");
} else {
printf("s1 > s2\n");
}
// Comparação parcial (primeiros 3 caracteres)
const char *t1 = "abcdef";
const char *t2 = "abcxyz";
int resultado2 = strncmp(t1, t2, 3); // 0 (abc == abc)
int resultado3 = strncmp(t1, t2, 4); // < 0 (abcd < abcxyz)
printf("strncmp(t1, t2, 3): %d\n", resultado2);
printf("strncmp(t1, t2, 4): %d\n", resultado3);
return 0;
}
Uso de Assert para Validação
A macro assert é útil para depuração, verificando condições que devem ser verdadeiras.
#include <stdio.h>
#include <assert.h>
#include <string.h>
char *strcpy_segura(char *destino, const char *fonte)
{
assert(destino != NULL && fonte != NULL);
char *inicio = destino;
while ((*destino = *fonte) != '\0') {
destino++;
fonte++;
}
return inicio;
}
int main()
{
char buffer[32];
const char *texto = "teste";
strcpy_segura(buffer, texto);
printf("Copiado: %s\n", buffer);
// Isto causará falha de asserção
// strcpy_segura(NULL, "teste");
return 0;
}
A asserção verifica se os ponteiros são válidos antes de prosseguir. Se a condição for falsa, o programa termina com mensagem de erro indicando a linha e arquivo.