Arrays de char, o terminador '\0' e as funções de string.h
Em Java, String é um objeto com métodos prontos como .length(), .toUpperCase() e .substring(). Em C, não existe um tipo string nativo. Uma string é simplesmente um array de char com uma convenção especial: o último caractere é sempre o valor zero, escrito como '\0' (o caractere nulo).
char terminado pelo caractere nulo '\0' (valor ASCII 0). Esse terminador é o que permite às funções saber onde a string acaba — sem ele, não há como distinguir o fim dos dados do que vem depois na memória.
A string "Olá" ocupa 4 bytes — 3 caracteres + 1 terminador:
| Índice | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| Caractere | 'O' |
'l' |
'á' |
'\0' |
| Valor ASCII | 79 | 108 | 225 | 0 |
n caracteres, o array precisa ter pelo menos n + 1 posições — a posição extra é para o '\0'. Declarar char nome[10] comporta até 9 caracteres úteis.
/* Forma 1 — literal de string (mais comum) */ char nome[20] = "Ana Souza"; // '\0' inserido automaticamente /* Forma 2 — array de char com inicializadores */ char saudacao[] = {'O','l','a','!',' ','C','\0'}; // '\0' explícito /* Forma 3 — declarar e preencher depois (sem inicialização inicial) */ char cidade[50]; cidade[0] = 'N'; cidade[1] = 'a'; cidade[2] = 't'; cidade[3] = '\0'; /* Forma 4 — tamanho inferido pelo compilador */ char linguagem[] = "C"; // tamanho = 2 (1 char + '\0') /* Forma 5 — ponteiro para string literal (SOMENTE LEITURA!) */ const char *aviso = "Não modifique!"; // armazenada em área de código
char s[] = "texto" cria um array na stack — você pode modificar os caracteres. Já const char *s = "texto" aponta para um literal na memória de código do programa, que é somente leitura. Tentar modificar via ponteiro causa comportamento indefinido (geralmente segfault). Use const para indicar essa intenção.
#include <stdio.h>
int main() {
char s[] = "Hi";
printf("s[0] = '%c' (%d)\n", s[0], s[0]); // 'H' (72)
printf("s[1] = '%c' (%d)\n", s[1], s[1]); // 'i' (105)
printf("s[2] = '%c' (%d)\n", s[2], s[2]); // '' (0) — terminador nulo
printf("sizeof(s) = %zu\n", sizeof(s)); // 3 bytes
return 0;
}
printf com %s
char nome[] = "Carlos";
printf("Nome: %s\n", nome); // Nome: Carlos
printf("Nome: %10s\n", nome); // alinhado à direita em campo de 10
printf("Nome: %-10s|\n", nome); // alinhado à esquerda
printf("3 primeiros: %.3s\n", nome); // precisão: limita caracteres exibidos
scanf vs. fgets
#include <stdio.h>
int main() {
char simples[50];
char frase[100];
/* scanf("%s"): lê até o primeiro espaço — não serve para frases */
printf("Seu nome (uma palavra): ");
scanf("%49s", simples); // 49 = tamanho - 1, evita buffer overflow
printf("Leu: %s\n", simples);
getchar(); // consome o '\n' que ficou no buffer
/* fgets: lê a linha inteira, incluindo espaços — preferido para frases */
printf("Uma frase completa: ");
fgets(frase, sizeof(frase), stdin);
printf("Leu: %s", frase); // fgets já inclui '\n' na string
return 0;
}
| Função | Lê espaços? | Segura? | Inclui '\n'? |
Quando usar |
|---|---|---|---|---|
scanf("%s") |
❌ Para no espaço | ⚠️ Só com limite: %49s |
❌ | Palavras únicas (usuário, senha) |
fgets |
✅ Lê tudo até '\n' |
✅ Sempre (limita com 2º argumento) | ✅ (pode remover manualmente) | Frases, nomes completos, linhas |
'\n' deixado por fgets:
fgets(frase, sizeof(frase), stdin);
/* Encontra e remove o '\n' final, se existir */
int len = strlen(frase);
if (len > 0 && frase[len - 1] == '\n')
frase[len - 1] = '\0';
<string.h>A biblioteca padrão oferece um conjunto de funções para manipular strings. Todas assumem que as strings são terminadas por '\0' — nunca as chame com arrays que não seguem essa convenção.
| Função | O que faz | Retorna |
|---|---|---|
strlen(s) |
Comprimento da string (sem o '\0') |
size_t (inteiro sem sinal) |
strcpy(dest, src) |
Copia src para dest (incluindo '\0') |
Ponteiro para dest |
strncpy(dest, src, n) |
Copia no máximo n bytes de src para dest |
Ponteiro para dest |
strcat(dest, src) |
Concatena src ao final de dest |
Ponteiro para dest |
strncat(dest, src, n) |
Concatena no máximo n chars de src |
Ponteiro para dest |
strcmp(s1, s2) |
Compara duas strings lexicograficamente | 0 se iguais, <0 se s1<s2, >0 se s1>s2 |
strncmp(s1, s2, n) |
Compara no máximo n caracteres |
Mesmo que strcmp |
strchr(s, c) |
Primeira ocorrência do char c em s |
Ponteiro para o char, ou NULL |
strstr(s, sub) |
Primeira ocorrência da substring sub em s |
Ponteiro para o início, ou NULL |
sprintf(buf, fmt, ...) |
Formata e escreve em uma string (como printf para string) |
Número de chars escritos |
strlen — Comprimento
#include <stdio.h>
#include <string.h>
int main() {
char s1[] = "Hello";
char s2[] = "Programação em C";
char s3[] = ""; // string vazia
printf("strlen(\"%s\") = %zu\n", s1, strlen(s1)); // 5
printf("strlen(\"%s\") = %zu\n", s2, strlen(s2)); // 16
printf("strlen(\"%s\") = %zu\n", s3, strlen(s3)); // 0
printf("sizeof(s1) = %zu\n", sizeof(s1)); // 6 (5 + '\0')
return 0;
}
strlen vs. sizeof: strlen(s) percorre os caracteres até o '\0' e retorna o comprimento lógico da string (sem o terminador). sizeof(s) retorna o tamanho físico do array em bytes (inclui o '\0'). Dentro de uma função, sizeof em parâmetro array retorna o tamanho do ponteiro (8 bytes) — use sempre strlen para comprimento de string.
strcpy e strncpy — Copiar= para copiar strings! Em C, destino = origem para arrays é erro de compilação; para ponteiros, copia apenas o endereço (não o conteúdo). Use sempre strcpy ou strncpy.
#include <stdio.h>
#include <string.h>
int main() {
char origem[] = "Natal, RN";
char destino[30];
/* strcpy: copia incluindo o '\0' */
strcpy(destino, origem);
printf("strcpy: \"%s\"\n", destino);
/* strncpy: copia no máximo n chars — mais seguro */
char buf[6];
strncpy(buf, origem, 5); // copia "Natal" (5 chars)
buf[5] = '\0'; // strncpy NÃO garante terminador se n == strlen(src)
printf("strncpy: \"%s\"\n", buf);
/* Padrão seguro recomendado */
char seguro[10];
strncpy(seguro, origem, sizeof(seguro) - 1);
seguro[sizeof(seguro) - 1] = '\0'; // garante terminador sempre
printf("seguro: \"%s\"\n", seguro);
return 0;
}
strncpy: Se o tamanho n for igual ao comprimento de src, o terminador '\0' não é copiado. Por isso o padrão profissional é sempre forçar o terminador: buf[n-1] = '\0'.
strcat e strncat — Concatenar
#include <stdio.h>
#include <string.h>
int main() {
char saudacao[50] = "Bom ";
char parte2[] = "dia, ";
char nome[] = "Carlos!";
strcat(saudacao, parte2); // "Bom dia, "
strcat(saudacao, nome); // "Bom dia, Carlos!"
printf("%s\n", saudacao);
/* strncat — concatena no máximo n chars (mais seguro) */
char resultado[15] = "Lingua";
strncat(resultado, "gem C avançada", 3); // adiciona só "gem"
resultado[sizeof(resultado)-1] = '\0';
printf("%s\n", resultado);
return 0;
}
strcat: strcat(dest, src) não verifica se há espaço suficiente em dest. Se dest não tiver espaço para src, a função escreve além do array — corrompendo memória. Sempre verifique se strlen(dest) + strlen(src) + 1 <= sizeof(dest) antes de chamar.
strcmp — CompararEm C, você nunca compara strings com == — isso compara endereços, não conteúdo. Use sempre strcmp:
#include <stdio.h>
#include <string.h>
int main() {
char s1[] = "banana";
char s2[] = "banana";
char s3[] = "cereja";
char s4[] = "abacaxi";
/* ❌ ERRADO — compara endereços, nunca conteúdo */
if (s1 == s2) printf("iguais\n"); // sempre falso (endereços diferentes!)
/* ✅ CORRETO — compara conteúdo */
printf("strcmp(s1, s2) = %d\n", strcmp(s1, s2)); // 0 (iguais)
printf("strcmp(s1, s3) = %d\n", strcmp(s1, s3)); // negativo (b < c)
printf("strcmp(s1, s4) = %d\n", strcmp(s1, s4)); // positivo (b > a)
/* Uso típico: verificar igualdade */
char senha[20];
printf("\nDigite a senha: ");
scanf("%19s", senha);
if (strcmp(senha, "abc123") == 0)
printf("Acesso concedido!\n");
else
printf("Senha incorreta.\n");
return 0;
}
strcmp: O valor exato de retorno (diferente de 0) varia entre implementações — o que importa é o sinal: zero = iguais, negativo = s1 vem antes de s2 alfabeticamente, positivo = s1 vem depois. Nunca compare com strcmp(s1, s2) == -1; use sempre < 0, == 0 ou > 0.
strchr e strstr — Buscar
#include <stdio.h>
#include <string.h>
int main() {
char frase[] = "Programar em C é divertido!";
/* strchr: procura um único caractere */
char *p = strchr(frase, 'C');
if (p != NULL)
printf("'C' encontrado na posição %ld: \"%s\"\n",
p - frase, p); // aritmética de ponteiros para achar o índice
char *espaco = strchr(frase, ' ');
if (espaco)
printf("Primeira palavra: \"%.*s\"\n",
(int)(espaco - frase), frase);
/* strstr: procura uma substring */
char *sub = strstr(frase, "em C");
if (sub != NULL)
printf("\"em C\" encontrado na posição %ld\n", sub - frase);
char *ausente = strstr(frase, "Java");
printf("\"Java\" encontrado? %s\n", ausente ? "Sim" : "Não");
return 0;
}
strchr e strstr retornam ponteiros para dentro da string original, a posição (índice) do elemento encontrado é resultado - inicio — aritmética de ponteiros que você estudou na Aula 21.
sprintf — Formatar em Stringsprintf funciona exatamente como printf, mas escreve em um buffer de char em vez de na tela. É a forma padrão de construir strings formatadas em C:
#include <stdio.h>
#include <string.h>
int main() {
char mensagem[100];
char linha[80];
int codigo = 1042;
char produto[] = "Notebook";
float preco = 3499.90f;
/* Constrói uma string formatada */
sprintf(mensagem, "Produto #%04d: %-15s R$ %.2f",
codigo, produto, preco);
printf("%s\n", mensagem);
/* Útil para montar nomes de arquivo dinamicamente */
int mes = 3, ano = 2026;
char arquivo[30];
sprintf(arquivo, "relatorio_%04d_%02d.txt", ano, mes);
printf("Arquivo: %s\n", arquivo);
/* Concatenar número em string */
int versao = 3;
char titulo[40];
sprintf(titulo, "Versão %d do sistema", versao);
printf("%s\n", titulo);
return 0;
}
sprintf: Se a string formatada for maior que o buffer, sprintf escreve além do array. Prefira snprintf(buf, sizeof(buf), fmt, ...), que limita o número de caracteres escritos — é a versão segura e recomendada em código de produção.
Para armazenar várias strings, há duas abordagens:
#include <stdio.h>
#include <string.h>
int main() {
/* 5 strings de no máximo 20 chars cada */
char cidades[5][20] = {
"Natal", "Recife", "Fortaleza", "João Pessoa", "Maceió"
};
int n = 5;
printf("Cidades do Nordeste:\n");
for (int i = 0; i < n; i++)
printf(" [%d] %s (%zu chars)\n", i, cidades[i], strlen(cidades[i]));
/* Ordenação simples por nome */
char temp[20];
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
if (strcmp(cidades[i], cidades[j]) > 0) {
strcpy(temp, cidades[i]);
strcpy(cidades[i], cidades[j]);
strcpy(cidades[j], temp);
}
printf("\nOrdenadas:\n");
for (int i = 0; i < n; i++)
printf(" %s\n", cidades[i]);
return 0;
}
#include <stdio.h>
int main() {
/* Array de ponteiros para strings literais — tamanhos variados */
const char *dias[] = {
"Segunda-feira", "Terça-feira", "Quarta-feira",
"Quinta-feira", "Sexta-feira", "Sábado", "Domingo"
};
int n = 7;
for (int i = 0; i < n; i++)
printf(" %d: %s\n", i + 1, dias[i]);
return 0;
}
| Aspecto | C | Java |
|---|---|---|
| Tipo | Array de char terminado por '\0' |
Objeto String imutável |
| Comprimento | strlen(s) |
s.length() |
| Copiar | strcpy(dest, src) |
String dest = src; (imutável — seguro) |
| Concatenar | strcat(dest, src) |
s1 + s2 ou s1.concat(s2) |
| Comparar | strcmp(s1, s2) == 0 |
s1.equals(s2) |
| Buscar caractere | strchr(s, c) |
s.indexOf(c) |
| Buscar substring | strstr(s, sub) |
s.contains(sub) ou s.indexOf(sub) |
| Imutabilidade | ❌ Mutável (pode modificar caracteres) | ✅ Imutável — toda "modificação" cria novo objeto |
| Risco de buffer overflow | ⚠️ Alto — use funções com limite (strncpy, snprintf) |
✅ Nenhum — JVM gerencia memória |
char s1[50] = "Olá, ";
char s2[] = "mundo!";
int len = strlen(s1);
strcat(s1, s2);
if (strcmp(s1, "Olá, mundo!") == 0)
printf("Iguais!\n");
char *p = strstr(s1, "mundo");
if (p) printf("%s\n", p);
String s1 = "Olá, ";
String s2 = "mundo!";
int len = s1.length();
String s3 = s1 + s2;
if (s3.equals("Olá, mundo!"))
System.out.println("Iguais!");
if (s3.contains("mundo"))
System.out.println(
s3.substring(s3.indexOf("mundo")));