Aula 26 — Strings em C

Arrays de char, o terminador '\0' e as funções de string.h

1. O que é uma String em C?

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).

Uma string em C é um array de 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.

Representação 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
⚠️ Tamanho do array ≠ comprimento da string. Para armazenar uma string de 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.

2. Declaração e Inicialização

Formas de declarar uma string

/* 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
⚠️ Ponteiro vs. array para strings: 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.

Verificando o terminador

#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;
}
s[0] = 'H' (72)
s[1] = 'i' (105)
s[2] = '' (0)
sizeof(s) = 3

3. Entrada e Saída de Strings

Saída — 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
Nome: Carlos
Nome:    Carlos
Nome: Carlos    |
3 primeiros: Car

Entrada — 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;
}
Seu nome (uma palavra): Ana Souza
Leu: Ana
Uma frase completa: Bom dia, mundo!
Leu: Bom dia, mundo!
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
Removendo o '\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';

4. As Principais Funções de <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

5. 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("Hello") = 5
strlen("Programação em C") = 16
strlen("") = 0
sizeof(s1) = 6
⚠️ 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.

6. strcpy e strncpy — Copiar

⚠️ Nunca use = 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;
}
strcpy: "Natal, RN"
strncpy: "Natal"
seguro: "Natal, RN"
Cuidado com 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'.

7. 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;
}
Bom dia, Carlos!
Linguagem
⚠️ Buffer overflow com 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.

8. strcmp — Comparar

Em 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(s1, s2) = 0
strcmp(s1, s3) = -2
strcmp(s1, s4) = 1

Digite a senha: abc123
Acesso concedido!
Interpretando o retorno de 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.

9. 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;
}
'C' encontrado na posição 13: "C é divertido!"
Primeira palavra: "Programar"
"em C" encontrado na posição 10
"Java" encontrado? Não
Padrão para extrair a posição: Como 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.

10. sprintf — Formatar em String

sprintf 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;
}
Produto #1042: Notebook R$ 3499.90
Arquivo: relatorio_2026_03.txt
Versão 3 do sistema
⚠️ Buffer overflow em 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.

11. Arrays de Strings

Para armazenar várias strings, há duas abordagens:

Abordagem 1 — Matriz de char (tamanho fixo)

#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;
}
Cidades do Nordeste:
  [0] Natal (5 chars)
  [1] Recife (6 chars)
  [2] Fortaleza (9 chars)
  [3] João Pessoa (11 chars)
  [4] Maceió (6 chars)

Ordenadas:
  Fortaleza
  João Pessoa
  Maceió
  Natal
  Recife

Abordagem 2 — Array de ponteiros (mais flexível)

#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;
}
  1: Segunda-feira
  2: Terça-feira
  3: Quarta-feira
  4: Quinta-feira
  5: Sexta-feira
  6: Sábado
  7: Domingo

12. C vs. Java — Strings

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

Side-by-side: operações comuns

C
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);
Java
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")));

Quiz — Aula 26

1. Quantos bytes ocupa o array char s[] = "Rio"?

2. Por que if (s1 == s2) não compara o conteúdo de duas strings em C?

3. Qual é a saída?

#include <stdio.h>
#include <string.h>
int main() {
    char s[] = "Hello";
    printf("%zu %zu", strlen(s), sizeof(s));
}

4. Qual das opções é a forma correta e segura de ler uma linha completa (com espaços) do teclado em C?

5. O que strncpy(dest, src, n) faz se strlen(src) == n?

6. Qual é a saída?

#include <stdio.h>
#include <string.h>
int main() {
    printf("%d\n", strcmp("abc", "abd"));
    printf("%d\n", strcmp("xyz", "xyz"));
}

7. O que strchr("Programar", 'a') retorna?

8. Qual a diferença principal entre const char *s = "texto" e char s[] = "texto"?