Manipulação de Strings em C: Um Guia Completo

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.

Tags: C Strings Memória malloc strcpy

Publicado em 7-7 21:20