Em C, strings são representadas como arrays de caracteres terminados por um caractere nulo ('\0'). A biblioteca string.h fornece funções essenciais para manipular essas strings, como copiar, concatenar, comparar e obter o comprimento. No entanto, muitas dessas funções são propensas a erros de segurança, como o buffer overflow. Nesta aula, exploraremos as funções mais comuns, suas limitações e alternativas mais seguras.

Dominar essas funções é fundamental para escrever código C robusto e seguro. Veremos exemplos práticos e discutiremos como evitar armadilhas comuns.

strlen, strcpy, strcat

strlen retorna o comprimento de uma string, excluindo o caractere nulo. strcpy copia uma string de origem para um destino, sobrescrevendo o conteúdo do destino. strcat concatena (anexa) uma string de origem ao final de uma string de destino. Ambas as funções assumem que o buffer de destino tem espaço suficiente para o resultado.

Exemplo de uso:

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello";
    char dest[20] = "World";
    
    printf("Comprimento de src: %zu\n", strlen(src));
    
    strcpy(dest, src);
    printf("Após strcpy: %s\n", dest);
    
    strcat(dest, "!");
    printf("Após strcat: %s\n", dest);
    
    return 0;
}

Saída:

Comprimento de src: 5
Após strcpy: Hello
Após strcat: Hello!

strcmp

strcmp compara duas strings lexicograficamente. Retorna 0 se forem iguais, um valor negativo se a primeira for menor e positivo se for maior. A comparação é baseada nos valores ASCII dos caracteres.

Exemplo:

#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    
    int result = strcmp(str1, str2);
    if (result == 0) {
        printf("Iguais\n");
    } else if (result < 0) {
        printf("str1 vem antes\n");
    } else {
        printf("str2 vem antes\n");
    }
    
    return 0;
}

Saída:

str1 vem antes

strncpy e segurança

strncpy é uma versão mais segura de strcpy, pois copia no máximo n caracteres. No entanto, strncpy não adiciona automaticamente o terminador nulo se o limite for atingido, o que pode causar problemas. Além disso, se a string de origem for maior que n, a string de destino não será terminada em nulo. Por isso, é recomendado usar strncpy e garantir que o último caractere seja nulo, ou usar funções como snprintf.

Exemplo seguro:

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, world!";
    char dest[6];
    
    strncpy(dest, src, sizeof(dest) - 1);
    dest[sizeof(dest) - 1] = '\0';  // Garante terminação nula
    
    printf("Dest: %s\n", dest);
    return 0;
}

Saída:

Dest: Hello

Buffer overflow

Buffer overflow ocorre quando se escreve além dos limites de um buffer, sobrescrevendo memória adjacente. Isso pode levar a falhas de segurança, como execução de código arbitrário. Funções como gets, strcpy e strcat são particularmente perigosas se o tamanho do destino não for verificado. Para evitar, sempre use funções com limite de tamanho (strncpy, strncat, snprintf) e nunca use gets. Além disso, utilize fgets para leitura de entrada.

Exemplo de buffer overflow:

#include <stdio.h>
#include <string.h>

int main() {
    char buffer[10];
    strcpy(buffer, "Esta string é muito longa e causará overflow!");
    printf("%s\n", buffer);
    return 0;
}

Esse código pode corromper a memória e causar comportamento indefinido.

Boas práticas

Sempre verifique o tamanho do buffer antes de copiar ou concatenar. Prefira funções como snprintf para formatar strings de forma segura. Use fgets em vez de gets. Documente as suposições sobre o tamanho das strings. Considere usar funções como strlcpy e strlcat (disponíveis em alguns sistemas) que garantem terminação nula.

Referências

Exercícios

  1. Escreva uma função que receba duas strings e retorne 1 se forem iguais, 0 caso contrário, sem usar strcmp.

    ✓ Resposta:
    int str_igual(char *a, char *b) {
        while (*a && *b) {
            if (*a != *b) return 0;
            a++;
            b++;
        }
        return *a == *b;
    }
  2. O que há de errado no código abaixo? Corrija-o.

    char nome[10];
    strcpy(nome, "João");
    printf("%s\n", nome);

    ✓ Resposta:

    O buffer nome tem tamanho 10, e a string "João" tem 5 caracteres (incluindo o '\0'?), mas o problema é que a string "João" contém caracteres acentuados que em UTF-8 podem ocupar mais bytes (ex.: 'ã' ocupa 2 bytes). Na verdade, "João" em UTF-8 tem 5 bytes + nulo = 6 bytes, o que cabe em 10. No entanto, o código é tecnicamente correto, mas se a string fosse maior que 9 caracteres, haveria overflow. Uma versão segura seria usar strncpy ou snprintf.

  3. Use strncpy para copiar a string "Programação em C" para um buffer de 15 bytes, garantindo terminação nula.

    ✓ Resposta:
    char buffer[15];
    strncpy(buffer, "Programação em C", sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';
  4. Explique por que gets é perigoso e qual função deve ser usada em seu lugar.

    ✓ Resposta:

    gets não verifica o tamanho do buffer, podendo causar buffer overflow. Deve ser substituída por fgets, que permite limitar o número de caracteres lidos.

  5. Escreva um programa que concatene duas strings de forma segura, usando snprintf.

    ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        char dest[20] = "Hello, ";
        char src[] = "world!";
        int len = snprintf(dest + strlen(dest), sizeof(dest) - strlen(dest), "%s", src);
        if (len >= sizeof(dest) - strlen(dest)) {
            printf("Buffer truncado\n");
        }
        printf("%s\n", dest);
        return 0;
    }