C Funções de String
Na aula Strings você viu que um texto em C é um array de char com um '\0' no fim, e que não dá para comparar com == nem trocar com =. Para essas tarefas existe uma caixa de ferramentas pronta, o arquivo string.h: funções para medir, copiar, juntar, comparar e procurar dentro de textos. Nesta aula você vai conhecer as principais, e principalmente quais são perigosas: algumas delas escrevem além do fim do array sem perguntar nada, e esse é um dos erros de segurança mais famosos da história da computação.
strlen: contar os caracteres (sem o'\0'), e a diferença parasizeof.- Copiar texto:
strcpy(perigoso),strncpy(traiçoeiro) esnprintf(o jeito seguro). - O estouro de buffer do
strcpy, visto de verdade com-fsanitize=address. - Juntar textos com
strcatestrncat. - Comparar com
strcmp: o que quer dizer o retorno menor que zero, zero ou maior que zero. - Procurar com
strchrestrstr, e quebrar um texto em pedaços comstrtok(com cuidado).
Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
A ideia: uma caixa de ferramentas para texto
Pense na caixa de ferramentas da casa da Ana: trena para medir, cola para juntar, lupa para procurar. O string.h é isso para strings. Para usar qualquer ferramenta dele, a primeira linha do programa (depois do stdio.h) é:
#include <string.h>
Todas as funções desta aula seguem duas regras, herdadas da aula anterior:
- Elas trabalham até o
'\0'. Se ele faltar, elas continuam pela memória afora. - Elas não sabem o tamanho dos seus arrays. Quando uma função escreve num array, cabe a você garantir que há espaço, ou escolher uma função que receba o tamanho.
Os nomes parecem siglas porque são: o C antigo limitava os nomes a poucas letras. Quase todos começam com str ("string"), e o resto é uma abreviação em inglês que a aula traduz.
1. strlen: quantos caracteres?
strlen = "string length", comprimento da string. Ela anda pelo array contando até achar o '\0', exatamente o laço que você escreveu na aula Strings.
#include <stdio.h>
#include <string.h>
int main(void) {
char item[20] = "arroz";
printf("strlen: %zu\n", strlen(item));
printf("sizeof: %zu\n", sizeof item);
printf("strlen(\"\"): %zu\n", strlen(""));
return 0;
}
$ gcc -std=c23 -Wall -Wextra tamanho.c -o tamanho
$ ./tamanho
strlen: 5 « a-r-r-o-z: o '\0' não entra na conta
sizeof: 20 « o tamanho do array, cheio ou não
strlen(""): 0 « string vazia
- strlen(item)
- Devolve o número de caracteres antes do
'\0'. O texto "arroz" usa 6 bytes do array (5 letras +'\0'), mas ostrlendiz 5. - %zu
- O resultado de
strlené do tiposize_t(um inteiro sem sinal próprio para tamanhos, aula Tamanho dos Tipos). O formato certo é%zu; com%do gcc avisa (veja "Quando dá errado"). - sizeof item
- Mede a fileira inteira: 20 bytes, por mais curto que seja o texto.
strlen(s) responde "quantas letras tem o texto agora?". sizeof s responde "quantos bytes cabem no array?". Para saber se um texto de n letras cabe, a conta é n + 1 <= sizeof s: o + 1 é o '\0', que o strlen não conta. Esse "+ 1" esquecido é a causa de muitos estouros.
2. Copiar: strcpy, strncpy e snprintf
Você viu que nome = "Bia"; não compila. Para colocar um texto dentro de um array já criado, é preciso copiar as letras, uma por uma, e o '\0'. Há três ferramentas, e elas não são igualmente seguras.
strcpy: copia tudo, sem perguntar
strcpy = "string copy". A ordem dos argumentos é a mesma de uma atribuição: destino primeiro, origem depois (como em destino = origem).
#include <stdio.h>
#include <string.h>
int main(void) {
char destino[10];
strcpy(destino, "feijão");
printf("%s\n", destino);
return 0;
}
$ gcc -std=c23 -Wall -Wextra copia.c -o copia $ ./copia feijão
Funcionou porque "feijão" cabe em 10 bytes (são 7 bytes mais o '\0': o ã ocupa dois, como explica Acentos e UTF-8). O problema é que o strcpy não sabe que o destino tem 10 bytes. Se a origem for maior, ele escreve além do fim. Veja com um texto que vem de fora do programa, como acontece de verdade.
O estouro de buffer
Neste programa, o item vem da linha de comando: o que a Ana digita depois do nome do programa chega em argv[1] (aula Argumentos da Linha de Comando). O programador reservou 8 bytes, achando que nenhum item teria nome comprido:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char destino[8];
if (argc < 2) {
return 1;
}
strcpy(destino, argv[1]);
printf("Item: %s\n", destino);
return 0;
}
$ gcc -std=c23 -Wall -Wextra estouro.c -o estouro « nenhum aviso $ ./estouro arroz Item: arroz $ ./estouro "biscoito recheado" *** stack smashing detected ***: terminated Abortado (imagem do núcleo gravada) ./estouro "biscoito recheado" $ echo $? 134
- nenhum aviso
- O compilador não tem como saber o tamanho do que a Ana vai digitar. (Quando a origem é um texto fixo no código, o gcc às vezes percebe e avisa com
-Wstringop-overflow; não conte com isso.) - *** stack smashing detected ***
- "Esmagamento da pilha detectado". O
strcpyescreveu 18 bytes (17 letras +'\0') num array de 8 e passou por cima de memória vizinha. O Ubuntu compila os programas com uma proteção (o stack protector, "protetor da pilha") que percebeu o estrago na hora de omainterminar e abortou o programa. - Abortado … 134
- O programa foi encerrado à força (código 134 = abortado).
E com um texto só um byte maior que o array? "biscoito" tem 8 letras: com o '\0', são 9 bytes num array de 8. A proteção não pegou nada:
$ ./estouro biscoito Item: biscoito « o '\0' foi escrito fora do array, e "funcionou"
Esse é o pior caso: o programa escreveu em memória que não era dele, e tudo parece certo. É o erro clássico do "+ 1" esquecido. Para ver o erro de verdade, compile com o AddressSanitizer (aula Sanitizers):
$ gcc -std=c23 -Wall -Wextra -g -fsanitize=address estouro.c -o estouro
$ ./estouro biscoito
=================================================================
==60068==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7b4e79cf0028 at pc 0x7f4e7c2bd2a9 bp 0x7ffe37812400 sp 0x7ffe37811bb8
WRITE of size 9 at 0x7b4e79cf0028 thread T0
#0 0x7f4e7c2bd2a8 in strcpy ../../../../src/libsanitizer/asan/asan_interceptors.cpp:575
#1 0x61832ae8c329 in main /home/ana/c/estouro.c:9
…
This frame has 1 object(s):
[32, 40) 'destino' (line 5) <== Memory access at offset 40 overflows this variable
…
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/ana/c/estouro.c:9 in main
… « mais linhas com o mapa da memória
- WRITE of size 9
- Uma escrita de 9 bytes: as 8 letras de "biscoito" mais o
'\0'. - #0 … in strcpy
- Quem escreveu foi o
strcpy… - #1 … in main /home/ana/c/estouro.c:9
- …chamado na linha 9 do seu programa.
- [32, 40) 'destino' … overflows this variable
destinoocupa 8 bytes (do 32 ao 39); a escrita chegou ao byte 40, um depois do fim.
Escrever além do fim de um array se chama estouro de buffer (buffer overflow). Um atacante que controla o texto consegue escolher o que vai parar na memória vizinha, e em muitos casos isso basta para tomar o controle do programa. Grande parte das falhas de segurança famosas em programas escritos em C começou com um strcpy, strcat, sprintf ou gets sem limite. Regra do guia: só use strcpy quando você tiver certeza de que a origem cabe (um texto fixo e curto, num destino que você mesmo dimensionou). Para todo o resto, use snprintf.
No editor, um strcpy que estoura costuma passar calado: o programa mostra o texto inteiro e termina normalmente. A memória do WebAssembly é organizada de outro jeito, e o editor não tem stack protector nem AddressSanitizer. Por isso a missão abaixo cobra o jeito seguro, e não "fazer o programa cair".
strncpy: tem limite, mas é traiçoeiro
strncpy ("string n copy", copiar no máximo n) recebe um limite. Parece a solução, mas tem uma armadilha: se a origem não couber, ele copia n bytes e não coloca o '\0'.
#include <stdio.h>
#include <string.h>
int main(void) {
char a[6];
char b[6];
strncpy(a, "pão", sizeof a);
strncpy(b, "biscoito", sizeof b);
printf("a = %s\n", a);
printf("b tem %zu bytes; o último vale %d\n", sizeof b, b[5]);
return 0;
}
$ gcc -std=c23 -Wall -Wextra strncpy.c -o strncpy
strncpy.c: In function ‘main’:
strncpy.c:8:5: warning: ‘strncpy’ output truncated copying 6 bytes from a string of length 8 [-Wstringop-truncation]
8 | strncpy(b, "biscoito", sizeof b);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ ./strncpy
a = pão
b tem 6 bytes; o último vale 105 « 105 é o 'i': não há '\0' em b
O aviso diz "a saída do strncpy foi cortada: copiou 6 bytes de uma string de tamanho 8". O b ficou com b i s c o i e nenhuma placa de fim: um printf("%s", b) leria além do array, como na seção 7 da aula Strings. Quem usa strncpy precisa sempre completar à mão: b[sizeof b - 1] = '\0';. É fácil esquecer.
snprintf: o jeito seguro
O snprintf ("string n print formatted") é o printf que, em vez de mostrar na tela, escreve num array, com limite. Ele sempre coloca o '\0' e nunca passa do tamanho que você informa:
#include <stdio.h>
int main(void) {
char item[9];
int precisa = snprintf(item, sizeof item, "%s", "biscoito recheado");
printf("Guardou: [%s]\n", item);
printf("O texto completo tinha %d caracteres.\n", precisa);
if (precisa >= (int)sizeof item) {
printf("Aviso: não coube, foi cortado.\n");
}
return 0;
}
- #include <stdio.h>
- O
snprintfmora nostdio.h, junto com oprintf, e não nostring.h. - item
- 1º argumento: o destino.
- sizeof item
- 2º argumento: o tamanho do destino, com o lugar do
'\0'. Use sempresizeofdo próprio array, para não errar a conta. - "%s", "biscoito recheado"
- O resto é igual ao
printf: um formato e os valores. Para copiar um texto, o formato é"%s". - int precisa = …
- O
snprintfdevolve quantos caracteres o texto completo teria. Se esse número for maior ou igual ao tamanho do array, o texto foi cortado. O(int)converte osizeofparainte evita um aviso de comparação entre tipos com e sem sinal.
$ gcc -std=c23 -Wall -Wextra snprintf.c -o snprintf
snprintf.c: In function ‘main’:
snprintf.c:5:48: warning: ‘%s’ directive output truncated writing 17 bytes into a region of size 9 [-Wformat-truncation=]
5 | int precisa = snprintf(item, sizeof item, "%s", "biscoito recheado");
| ^~ ~~~~~~~~~~~~~~~~~~~
snprintf.c:5:19: note: ‘snprintf’ output 18 bytes into a destination of size 9
5 | int precisa = snprintf(item, sizeof item, "%s", "biscoito recheado");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ ./snprintf
Guardou: [biscoito]
O texto completo tinha 17 caracteres.
Aviso: não coube, foi cortado.
O gcc percebeu que o texto fixo não cabe e avisou ("a saída de %s foi cortada: 17 bytes numa região de 9"). Repare na diferença: com snprintf o corte é seguro (8 letras + '\0', nada escrito fora do array); o aviso só pergunta se você quis mesmo cortar. O conserto, se não quis, é aumentar o array.
E como ele é um printf, também monta textos com números, o que nenhuma função do string.h faz:
$ cat snprintf2.c
#include <stdio.h>
int main(void) {
char nome[] = "Ana";
int idade = 29;
char ficha[40];
snprintf(ficha, sizeof ficha, "%s (%d anos)", nome, idade);
printf("Ficha: %s\n", ficha);
return 0;
}
$ gcc -std=c23 -Wall -Wextra snprintf2.c -o snprintf2
$ ./snprintf2
Ficha: Ana (29 anos)
Missão: este programa copia pedido para um array de 9 bytes com strcpy, e estoura. No editor ele parece funcionar (mostra o texto inteiro), mas está escrevendo fora do array. Troque o strcpy por snprintf, com o tamanho do destino, para guardar com segurança só o que cabe: [biscoito].
#include <stdio.h>
#include <string.h>
int main(void) {
char pedido[] = "biscoito recheado";
char item[9];
strcpy(item, pedido);
printf("[%s]\n", item);
return 0;
}
#include <stdio.h>
#include <string.h>
int main(void) {
char pedido[] = "biscoito recheado";
char item[9];
snprintf(item, sizeof item, "%s", pedido);
printf("[%s]\n", item);
return 0;
}
Qual usar para copiar
| Função | Sabe o tamanho do destino? | Sempre põe o '\0'? | Quando usar |
|---|---|---|---|
strcpy(dest, orig) | Não | Sim (se couber) | Só quando a origem é fixa e você sabe que cabe. |
strncpy(dest, orig, n) | Sim | Não, se a origem for grande | Evite. Se usar, termine com dest[n - 1] = '\0';. |
snprintf(dest, sizeof dest, "%s", orig) | Sim | Sim | Padrão do guia. Também monta textos com números. |
3. Juntar: strcat e strncat
strcat = "string concatenate", concatenar (encadear, juntar no fim). Ela acha o '\0' do destino e copia a origem a partir dali:
#include <stdio.h>
#include <string.h>
int main(void) {
char frase[30] = "Lista: ";
strcat(frase, "arroz");
strcat(frase, ", feijão");
printf("%s\n", frase);
printf("%zu de %zu bytes usados\n", strlen(frase) + 1, sizeof frase);
return 0;
}
$ gcc -std=c23 -Wall -Wextra juntar.c -o juntar $ ./juntar Lista: arroz, feijão 22 de 30 bytes usados
- char frase[30] = "Lista: ";
- O destino precisa ter espaço sobrando desde o começo: o
strcatnão aumenta o array. - strcat(frase, "arroz");
- Gruda "arroz" depois de "Lista: ", passando por cima do
'\0'antigo e pondo um novo no fim. - strlen(frase) + 1
- Letras +
'\0': os bytes realmente ocupados.
O strcat tem o mesmo defeito do strcpy: não sabe o tamanho do destino. O strncat recebe um limite, mas o limite é o número de letras que ainda cabem, e a conta é sua:
#include <stdio.h>
#include <string.h>
int main(void) {
char frase[16] = "Lista: ";
size_t sobra = sizeof frase - strlen(frase) - 1;
strncat(frase, "arroz, feijão, café", sobra);
printf("[%s]\n", frase);
return 0;
}
$ gcc -std=c23 -Wall -Wextra strncat.c -o strncat $ ./strncat [Lista: arroz, f] « 15 letras + '\0' = os 16 bytes do array
A conta sizeof frase - strlen(frase) - 1 é "o tamanho do array, menos o que já está ocupado, menos o lugar do '\0'" (o strncat, diferente do strncpy, sempre põe o '\0'). Errar essa conta por 1 é muito comum. Uma alternativa sem conta é montar a frase inteira de uma vez com snprintf(frase, sizeof frase, "Lista: %s, %s", a, b);.
4. Comparar: strcmp
strcmp = "string compare". Ela compara letra por letra, do começo, e devolve um número:
Resultado de strcmp(a, b) | Significa |
|---|---|
| 0 | As duas strings são iguais |
| menor que 0 (negativo) | a vem antes de b (na ordem dos códigos dos caracteres) |
| maior que 0 (positivo) | a vem depois de b |
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "ana", b[] = "bia", c[] = "Bia", d[] = "anas", e[] = "carlos";
printf("ana x ana = %d\n", strcmp(a, a));
printf("ana x bia = %d\n", strcmp(a, b));
printf("bia x ana = %d\n", strcmp(b, a));
printf("Bia x ana = %d\n", strcmp(c, a));
printf("ana x anas = %d\n", strcmp(a, d));
printf("ana x carlos = %d\n", strcmp(a, e));
return 0;
}
$ gcc -std=c23 -Wall -Wextra comparar.c -o comparar $ ./comparar ana x ana = 0 « iguais ana x bia = -1 « 'a' (97) vem antes de 'b' (98) bia x ana = 1 Bia x ana = -31 « 'B' (66) vem antes de 'a' (97): maiúsculas vêm antes! ana x anas = -115 « "ana" acabou antes: o '\0' (0) vem antes do 's' (115) ana x carlos = -2
Lendo a saída:
- A comparação para na primeira letra diferente e compara os códigos das duas. Aqui o resultado foi justamente a diferença entre eles (66 − 97 = −31).
- Não confie no número exato, só no sinal. A regra do C só garante "negativo, zero ou positivo". Quando os dois textos estão escritos direto no código (
strcmp("Bia", "ana")), o gcc calcula a resposta na compilação e o programa mostra-1, e não-31. Outras bibliotecas devolvem outros números. - A ordem é a dos códigos, não a do dicionário: todas as maiúsculas (65 a 90) vêm antes de todas as minúsculas (97 a 122). Por isso "Bia" vem antes de "ana". Letras acentuadas ficam depois de tudo (veja Acentos e UTF-8).
O uso mais comum: igual ou não?
Na prática, quase sempre a pergunta é "são iguais?", e a resposta é strcmp(a, b) == 0:
#include <stdio.h>
#include <string.h>
int main(void) {
char digitada[20];
printf("Senha: ");
if (scanf("%19s", digitada) != 1) {
return 1;
}
if (strcmp(digitada, "lojinha") == 0) {
printf("Bem-vinda, Ana!\n");
} else {
printf("Senha errada.\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra senha.c -o senha $ echo lojinha | ./senha Senha: Bem-vinda, Ana! $ echo Lojinha | ./senha Senha: Senha errada. « maiúscula conta: 'L' não é 'l'
if (strcmp(a, b)) é "se forem diferentes"
Como iguais dão 0, e 0 é "falso" em C, escrever if (strcmp(nome, "Ana")) entra no if quando os textos são diferentes. É o contrário do que parece. Escreva sempre a comparação inteira: == 0 para iguais, != 0 para diferentes.
Existe também o strncmp(a, b, n), que compara só os n primeiros caracteres: strncmp(email, "ana", 3) == 0 pergunta "começa com ana?".
Missão: a senha digitada (o editor já envia lojinha) está certa, mas o programa responde "Senha errada.". Leia o aviso do compilador e troque o == por uma comparação com strcmp, para mostrar Bem-vinda, Ana!.
#include <stdio.h>
#include <string.h>
int main(void) {
char digitada[20];
printf("Senha: ");
if (scanf("%19s", digitada) != 1) {
return 1;
}
if (digitada == "lojinha") {
printf("Bem-vinda, Ana!\n");
} else {
printf("Senha errada.\n");
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main(void) {
char digitada[20];
printf("Senha: ");
if (scanf("%19s", digitada) != 1) {
return 1;
}
if (strcmp(digitada, "lojinha") == 0) {
printf("Bem-vinda, Ana!\n");
} else {
printf("Senha errada.\n");
}
return 0;
}
5. Procurar: strchr e strstr
strchr = "string character": procura um caractere. strstr = "string string": procura um texto dentro de outro. As duas devolvem o lugar onde acharam (um ponteiro, assunto de Ponteiros e Strings), ou NULL ("nulo", nenhum lugar) se não acharam.
#include <stdio.h>
#include <string.h>
int main(void) {
char email[] = "ana@lojinha.com";
char *arroba = strchr(email, '@');
if (arroba != NULL) {
printf("Achou '@' na posição %d\n", (int)(arroba - email));
printf("Domínio: %s\n", arroba + 1);
}
if (strchr(email, '#') == NULL) {
printf("Não tem '#'\n");
}
char *fim = strstr(email, ".com");
if (fim != NULL) {
printf("\".com\" começa na posição %d\n", (int)(fim - email));
}
return 0;
}
- char *arroba = strchr(email, '@');
- Procura o caractere
'@'(aspas simples: é umchar). Ochar *é uma variável que guarda um lugar dentro do texto. Por enquanto, leia como "marcador de página". - if (arroba != NULL)
- Sempre confira antes de usar: se não achou, o resultado é
NULL, e usar umNULLderruba o programa (aula Ponteiro Nulo). - arroba - email
- A distância entre o lugar achado e o começo do texto: a posição (índice). O
(int)converte para usar com%d. - arroba + 1
- O lugar logo depois do
@. Um%sa partir dali mostra o resto do texto até o'\0'. - strstr(email, ".com")
- Mesma ideia, procurando um pedaço de texto inteiro (aspas duplas).
$ gcc -std=c23 -Wall -Wextra procurar.c -o procurar $ ./procurar Achou '@' na posição 3 Domínio: lojinha.com Não tem '#' ".com" começa na posição 11
Se só interessa saber se tem, basta a comparação: if (strchr(email, '@') != NULL). Para achar a última ocorrência de um caractere, existe strrchr (o r é de reverse, "do fim para o começo"): útil para achar a extensão em "foto.final.png".
6. Quebrar em pedaços: strtok (com cuidado)
strtok = "string token", e token é cada pedaço de um texto separado por algum caractere. Serve para ler uma lista como "arroz,feijão,café":
#include <stdio.h>
#include <string.h>
int main(void) {
char lista[] = "arroz,feijão,,café";
char *item = strtok(lista, ",");
while (item != NULL) {
printf("[%s]\n", item);
item = strtok(NULL, ",");
}
printf("Depois, lista = \"%s\"\n", lista);
return 0;
}
- strtok(lista, ",")
- A primeira chamada recebe o texto e os separadores (aqui, só a vírgula;
" ,"aceitaria espaço ou vírgula). Devolve o primeiro pedaço. - strtok(NULL, ",")
- As chamadas seguintes recebem
NULLno lugar do texto: quer dizer "continue de onde parou". Ostrtokguarda escondido onde parou. - while (item != NULL)
- Quando não há mais pedaços, ele devolve
NULLe o laço termina.
$ gcc -std=c23 -Wall -Wextra tokens.c -o tokens $ ./tokens [arroz] [feijão] [café] « o pedaço vazio entre ",," sumiu Depois, lista = "arroz" « a lista original foi alterada
Funciona, mas repare nos quatro cuidados:
- Ele altera o texto. Para separar os pedaços, o
strtoktroca cada vírgula por um'\0'. Por isso, depois,listamostra só "arroz". Se você ainda precisar do original, faça uma cópia antes. - Separadores seguidos viram um só. O campo vazio entre
,,desapareceu. Numa planilha, isso desalinha as colunas. - Não funciona num literal. Com
char *lista = "arroz,feijão";, ele tenta escrever o'\0'na memória só de leitura: no laboratório,Falha de segmentação (imagem do núcleo gravada) ./tokliteral. Use um array (char lista[] = …). - Um texto por vez. Como ele guarda escondido onde parou, não dá para quebrar duas strings ao mesmo tempo (por exemplo, linhas e, dentro de cada linha, colunas). No Linux existe o
strtok_r, que guarda essa posição numa variável sua.
Quando dá errado
error: implicit declaration of function ‘strlen’ [-Wimplicit-function-declaration]
O que significa: faltou o #include <string.h>. O compilador não conhece a função. No gcc 15 isso é um erro, e ele mesmo mostra a linha que falta:
$ gcc -std=c23 -Wall -Wextra semh.c -o semh
semh.c: In function ‘main’:
semh.c:5:21: error: implicit declaration of function ‘strlen’ [-Wimplicit-function-declaration]
5 | printf("%zu\n", strlen(item));
| ^~~~~~
semh.c:2:1: note: include ‘<string.h>’ or provide a declaration of ‘strlen’
1 | #include <stdio.h>
+++ |+#include <string.h>
2 |
Como consertar: o +++ |+ é a sugestão do gcc: acrescente #include <string.h> no topo. (O snprintf é do stdio.h.)
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ {aka ‘long unsigned int’}
O que significa: você mostrou o resultado de strlen (ou de sizeof) com %d. O gcc até sugere %ld, mas o formato próprio de size_t é %zu.
Como consertar: printf("%zu letras\n", strlen(item));.
*** stack smashing detected ***: terminated ou texto estranho depois de um strcpy/strcat
O que significa: estouro de buffer: a origem não cabia no destino.
Como consertar: troque por snprintf(dest, sizeof dest, "%s", orig) e confira o retorno se cortar for um problema. Para achar a linha, compile com -g -fsanitize=address.
warning: ‘strncpy’ output truncated … [-Wstringop-truncation]
O que significa: o strncpy cortou a origem e o destino ficou sem '\0'.
Como consertar: use snprintf. Se precisar manter o strncpy, termine com dest[sizeof dest - 1] = '\0';.
if com strcmp faz o contrário do esperado
O que significa: você escreveu if (strcmp(a, b)), que é verdadeiro quando as strings são diferentes. No laboratório, if (strcmp(nome, "Ana")) com nome valendo "Ana" mostrou "Não é a Ana.".
Como consertar: if (strcmp(a, b) == 0) para iguais.
Falha de segmentação com strchr, strstr ou strtok
O que significa: ou o resultado NULL (não achou) foi usado sem conferir, ou o strtok recebeu um literal (char *s = "…"), que não pode ser alterado.
Como consertar: confira != NULL antes de usar o resultado; para o strtok, use um array char s[] = "…".
Referência rápida
| Função | Nome em inglês | O que faz | Devolve |
|---|---|---|---|
strlen(s) | string length | Conta os caracteres até o '\0' (sem ele) | size_t (use %zu) |
strcpy(d, o) | string copy | Copia o para d, com '\0'. Sem limite | d |
strncpy(d, o, n) | string n copy | Copia até n bytes; pode deixar sem '\0' | d |
snprintf(d, sizeof d, "%s", o) | string n print formatted | Copia/monta com limite; sempre põe '\0' (stdio.h) | Tamanho que o texto completo teria |
strcat(d, o) | string concatenate | Junta o no fim de d. Sem limite | d |
strncat(d, o, n) | string n concatenate | Junta até n letras e põe '\0'; n = espaço que sobra − 1 | d |
strcmp(a, b) | string compare | Compara letra por letra | < 0, 0 (iguais) ou > 0 |
strncmp(a, b, n) | string n compare | Compara só os n primeiros | < 0, 0 ou > 0 |
strchr(s, c) | string character | Procura a primeira ocorrência do caractere c | O lugar, ou NULL |
strrchr(s, c) | string reverse character | Procura a última ocorrência de c | O lugar, ou NULL |
strstr(s, t) | string string | Procura o texto t dentro de s | O lugar, ou NULL |
strtok(s, sep) | string token | Quebra em pedaços; altera s; depois, strtok(NULL, sep) | O próximo pedaço, ou NULL |
A lista completa, com as funções de memória (memcpy, memset…), está na Referência do string.h.
Exercícios
1. O cabeçalho
Complete a linha que traz as funções desta aula:
2. Contar
Quanto vale strlen("café")? Dica: o é ocupa 2 bytes. Escreva só o número.
3. Copiar com segurança
Complete a cópia segura de origem para destino:
4. O que strcmp("casa", "casa") devolve?
5. Qual é o problema de char b[4]; strncpy(b, "banana", sizeof b);?
6. strchr(email, '@') devolve NULL. O que isso quer dizer?
7. Depois de quebrar char s[] = "a;b;c"; com strtok(s, ";") até o fim, o que printf("%s", s) mostra?
Missão final: a etiqueta do cliente
Missão: a lojinha imprime etiquetas no formato sobrenome, nome. Monte etiqueta com snprintf (formato "%s, %s"), conte as letras com strlen e use strchr para conferir se o e-mail tem @. Sem avisos.
Etiqueta: Souza, Ana (10 letras) E-mail válido? sim
#include <stdio.h>
#include <string.h>
int main(void) {
char nome[] = "Ana";
char sobrenome[] = "Souza";
char email[] = "ana@lojinha.com";
char etiqueta[30] = "";
// 1. monte "Souza, Ana" em etiqueta
// 2. mostre a etiqueta e o número de letras
printf("Etiqueta: %s (? letras)\n", etiqueta);
// 3. o e-mail tem '@'?
printf("E-mail válido? ?\n");
return 0;
}
#include <stdio.h>
#include <string.h>
int main(void) {
char nome[] = "Ana";
char sobrenome[] = "Souza";
char email[] = "ana@lojinha.com";
char etiqueta[30] = "";
snprintf(etiqueta, sizeof etiqueta, "%s, %s", sobrenome, nome);
printf("Etiqueta: %s (%zu letras)\n", etiqueta, strlen(etiqueta));
if (strchr(email, '@') != NULL) {
printf("E-mail válido? sim\n");
} else {
printf("E-mail válido? não\n");
}
return 0;
}
A vírgula e o espaço contam como caracteres: "Souza, Ana" tem 10. Ter um @ está longe de garantir que um e-mail é válido, mas é a primeira conferência que todo formulário faz (mais em Validar a Entrada).
Resumo
#include <string.h>traz as funções de texto. Todas trabalham até o'\0'e nenhuma sabe o tamanho dos seus arrays.strlenconta as letras sem o'\0'(resultadosize_t, formato%zu); para caber, é precisostrlen + 1bytes.strcpyestrcatnão têm limite: com origem grande, causam estouro de buffer (stack smashing, ou pior, nada aparente).-fsanitize=addressmostra a linha.strncpypode deixar o destino sem'\0';strncatexige a conta do espaço que sobra. O jeito seguro de copiar e montar textos ésnprintf(dest, sizeof dest, "…", …).strcmpdevolve 0 para iguais, negativo se o primeiro vem antes, positivo se vem depois; confie só no sinal. Escreva sempre== 0.strchr/strstrdevolvem o lugar achado ouNULL(confira antes de usar).strtokaltera o texto, junta separadores seguidos e não funciona em literais.