Menu

Funções de string em C: strlen, strcpy, strcat, strcmp e mais

Um passeio prático pelo <string.h>: medir com strlen, copiar com strcpy e strncpy, juntar com strcat, comparar com strcmp, buscar com strchr e strstr - e como dimensionar buffers para que nenhuma delas estoure.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Toda função de <string.h> é um pequeno laço sobre um array de char que para no terminador nulo. Uma vez que isso fica claro, a biblioteca deixa de parecer uma lista de nomes crípticos e passa a parecer o punhado de laços que você mesmo teria escrito. Esta página cobre as que fazem quase todo o trabalho real - e as regras de dimensionamento que as impedem de escrever além do fim dos seus buffers.

Tudo aqui precisa de um include:

#include <string.h>

strlen: quantos caracteres

O strlen conta bytes até o terminador, sem incluí-lo. Ele devolve size_t, então imprima com %zu.

O strlen é 6; o sizeof é 32. O primeiro é o texto, o segundo é o armazenamento - veja strings para entender por que essa distinção importa tanto.

O strlen percorre a string inteira a cada chamada, então não o coloque numa condição de laço sobre a mesma string que não muda:

/* reconta a string inteira a cada iteracao */
for (size_t i = 0; i < strlen(s); i++) { ... }

/* conta uma vez */
size_t n = strlen(s);
for (size_t i = 0; i < n; i++) { ... }

strcpy e strncpy: copiando

strcpy(dst, src) copia os caracteres mais o terminador. Ele não faz ideia de quão grande é dst, então o chamador precisa garantir que dst comporte pelo menos strlen(src) + 1 bytes.

O strncpy recebe uma contagem máxima, mas tem uma armadilha famosa: quando a origem tem pelo menos n bytes, ele copia exatamente n caracteres e nenhum terminador. Sempre termine a string você mesmo:

O sizeof dst - 1 mais o terminador explícito é o padrão a memorizar. (O strncpy também completa origens curtas com zeros até n bytes, o que é trabalho desperdiçado em buffers grandes - ele foi projetado para registros de largura fixa, não para segurança.)

strcat e strncat: juntando

strcat(dst, src) acrescenta src ao final do que já estiver em dst. O destino precisa comportar as duas strings mais um terminador, e já precisa conter uma string válida - acrescentar a memória não inicializada é comportamento indefinido.

Repare no char path[64] = "/home/ada"; em vez de char path[64]; - o inicializador é o que o torna uma string válida para o primeiro strcat.

strncat(dst, src, n) acrescenta no máximo n caracteres e sempre adiciona um terminador, então n é o espaço restante, não o tamanho total do buffer:

strncat(dst, src, sizeof dst - strlen(dst) - 1);

Chamadas repetidas de strcat varrem o destino de novo para achar o fim dele toda vez. Construir uma string longa num laço desse jeito é quadrático; para qualquer coisa de tamanho relevante, controle a sua própria posição de escrita ou use snprintf, tratado em conversão de strings.

strcmp: comparando

O == compara endereços, então é falso para dois arrays distintos guardando o mesmo texto. O strcmp compara os caracteres e devolve o sinal da diferença.

Três regras:

  • Teste strcmp(a, b) == 0 para igualdade. Escrever if (strcmp(a, b)) significa "se elas diferem", que se lê ao contrário para quase todo mundo.
  • Só o sinal é definido. Não compare o resultado com 1 ou -1.
  • A ordenação é por valor de byte, então "Zebra" vem antes de "abacate" em ASCII. Para comparação sem distinção de maiúsculas, passe as duas cópias para minúsculas antes - o strcasecmp que se vê muito por aí é uma extensão POSIX, não C padrão.

strncmp(a, b, n) compara apenas os primeiros n caracteres, que é a forma limpa de testar um prefixo:

strchr e strstr: buscando

strchr(s, ch) encontra a primeira ocorrência de um caractere; strrchr encontra a última. strstr(palheiro, agulha) encontra uma substring. Os três devolvem um ponteiro para dentro da string original, ou NULL quando não há correspondência.

Subtrair o ponteiro devolvido do início dá o índice; o tipo dele é ptrdiff_t, impresso com %td. Como o resultado aponta para dentro do array original, at + 1 é o resto da string sem cópia nenhuma - um idioma muito comum em C.

Sempre verifique NULL antes de desreferenciar. strchr(email, '@') + 1 numa string sem @ calcula um endereço a partir de NULL e o programa deixa de ser definido.

Dividindo uma string

Não existe uma função de split, mas strchr mais um terminador resolvem no próprio lugar. Este exemplo corta um e-mail em usuário e domínio:

O memcpy copia uma quantidade exata de bytes sem se importar com terminadores, que é o que você quer quando já conhece o comprimento. O terminador é então escrito à mão.

Um exemplo trabalhado: normalizando um nome

Juntando o passeio - aparar, comparar, copiar e juntar:

Toda escrita é limitada pelo sizeof do próprio destino, e todo strncpy é seguido por um terminador explícito. Essa disciplina, aplicada com consistência, é o que torna seguro o código de strings em C.

Regras de dimensionamento para guardar

  • O strcpy precisa de strlen(src) + 1 bytes livres no destino.
  • O strcat precisa de strlen(dst) + strlen(src) + 1.
  • Depois de um strncpy, escreva dst[n - 1] = '\0' você mesmo.
  • Para o strncat, a contagem é o espaço que sobra: sizeof dst - strlen(dst) - 1.
  • Verifique NULL no strchr e no strstr antes de usar o resultado.
  • Use sizeof dst só quando dst for um array de verdade. Dentro de uma função que recebe char *dst, o sizeof dá o tamanho do ponteiro - passe o comprimento do buffer como parâmetro separado.

Perguntas frequentes

O que o strcmp devolve em C?

Zero quando as duas strings são idênticas, um valor negativo quando a primeira vem antes da segunda na ordenação, e positivo quando vem depois. Só o sinal tem significado - nunca suponha que ele devolve -1 ou 1. O teste que você quer é quase sempre if (strcmp(a, b) == 0).

Por que não posso comparar strings com == em C?

a == b compara os dois endereços, não os caracteres. Dois arrays separados guardando "gato" ficam em endereços diferentes, então a comparação é falsa mesmo com o texto igual. Use strcmp(a, b) == 0, de <string.h>.

Qual a diferença entre strcpy e strncpy?

O strcpy copia até o '\0' da origem sem ideia de quão grande é o destino. O strncpy para depois de no máximo n bytes - mas, se a origem tiver esse comprimento, ele copia nenhum terminador, então você precisa escrever dst[n - 1] = '\0'; por conta própria. Nenhum dos dois é automaticamente seguro; a disciplina de tamanho é sua.

Como encontrar uma substring em C?

strstr(palheiro, agulha) devolve um ponteiro para a primeira ocorrência, ou NULL se não houver nenhuma. Como ele devolve um ponteiro para dentro da string original, achou - palheiro dá o índice. Use strchr quando estiver procurando um único caractere.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR