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) == 0para igualdade. Escreverif (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
1ou-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 - ostrcasecmpque 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
strcpyprecisa destrlen(src) + 1bytes livres no destino. - O
strcatprecisa destrlen(dst) + strlen(src) + 1. - Depois de um
strncpy, escrevadst[n - 1] = '\0'você mesmo. - Para o
strncat, a contagem é o espaço que sobra:sizeof dst - strlen(dst) - 1. - Verifique
NULLnostrchre nostrstrantes de usar o resultado. - Use
sizeof dstsó quandodstfor um array de verdade. Dentro de uma função que recebechar *dst, osizeofdá 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.