Menu

Strings em C: arrays de char, o terminador nulo e exemplos

O C não tem um tipo string - uma string é um array de char que termina num byte '\0'. Esta página constrói esse modelo mental e depois cobre literais, impressão, percorrimento e por que você não pode copiar uma string com =.

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

A maioria das linguagens lhe dá um tipo string que conhece o próprio comprimento. O C não. Em C uma string é apenas um array de char com uma regra anexa: o texto é seguido por um byte de valor zero, escrito '\0' e chamado de terminador nulo. Todo o resto sobre strings em C decorre dessa única convenção.

Uma string é um array de char mais '\0'

Declare uma string inicializando um array de char a partir de um literal:

O array tem quatro bytes, não três:

name:  +-----+-----+-----+------+
       | 'A' | 'd' | 'a' | '\0' |
       +-----+-----+-----+------+
indice:   0     1     2     3

sizeof(name) é 4. O compilador contou os caracteres, somou um para o terminador e dimensionou o array para você. Esse byte extra é a razão de char name[3] = "Ada"; ser um bug esperando para acontecer - o texto cabe, mas o terminador não, então nada adiante consegue saber onde a string termina.

Quando você quer espaço para mudar o conteúdo depois, dê o tamanho você mesmo e deixe folga:

char name[32] = "Ada";   /* 3 caracteres, um terminador, 28 bytes de sobra */

Os bytes não usados são preenchidos com zero pelo inicializador, o que é inofensivo.

Por que o terminador importa

Nenhuma função do C recebe um comprimento. printf("%s", s) recebe um endereço e caminha adiante imprimindo bytes até encontrar '\0'. Então um array de char sem terminador não é uma string, e tratá-lo como uma lê qualquer memória que por acaso venha em seguida.

A regra para memorizar: o sizeof dá o armazenamento, o strlen dá o texto. Para char buf[32] = "Ada", sizeof buf é 32 e strlen(buf) é 3. Eles respondem a perguntas diferentes, e confundi-los é um dos bugs mais comuns do C.

Literais de string vs arrays de char

Estas duas linhas parecem semelhantes e se comportam de forma bem diferente:

char  a[] = "hello";   /* um array seu, inicializado a partir do literal */
char *p   = "hello";   /* um ponteiro apontado para o proprio literal */

a é um array de 6 bytes com uma cópia privada do texto. Você pode modificá-lo. p aponta para os dados de string somente leitura do programa; o literal é compartilhado, e escrever por p é comportamento indefinido que tipicamente trava em tempo de execução, e não em tempo de compilação.

Escreva const char * sempre que um ponteiro apontar para um literal. O compilador então rejeita a escrita em tempo de compilação em vez de deixá-la virar um travamento. Escolher entre os dois é, na verdade, uma questão sobre ponteiros e arrays, tratada em ponteiros e arrays.

Mais uma diferença que vale conhecer: sizeof a é 6, mas sizeof p é o tamanho de um ponteiro (8 na maioria dos sistemas), não importa o comprimento do texto.

Imprimindo e lendo strings

%s imprime uma string; %c imprime um único caractere.

Para entrada, evite o gets por completo - ele foi removido da linguagem porque não pode ser usado com segurança. Use fgets, que recebe o tamanho do buffer e não vai estourar:

O fgets mantém a quebra de linha que você digitou, que quase nunca é o que você quer na string. A linha do strcspn acha o primeiro '\n' e o sobrescreve com um terminador - uma forma compacta e padrão de aparar isso.

Percorrendo uma string caractere a caractere

Como o fim é marcado em vez de contado, o laço idiomático testa o próprio caractere:

A condição text[i] != '\0' é muitas vezes abreviada para apenas text[i], já que '\0' é zero e portanto falso. As duas estão corretas; a forma explícita é mais fácil de ler enquanto você está aprendendo.

Você pode percorrer com um ponteiro em vez de um índice, o que é igualmente idiomático em C:

A conversão para unsigned char antes de chamar toupper não é enfeite: as funções de <ctype.h> são indefinidas para valores negativos, e um char puro pode ser negativo para bytes acima de 127.

Você não pode copiar uma string com =

Este é o muro em que todo novato bate:

char a[10];
a = "hello";        /* erro: atribuicao a expressao de tipo array */

char b[10] = "hi";
char c[10];
c = b;              /* erro - mesma razao */

O nome de um array não é um valor modificável, então o = não tem o que fazer. O único lugar em que o atalho funciona é a inicialização na declaração, que é uma operação diferente, feita pelo compilador.

Para copiar em tempo de execução, copie os bytes com strcpy, de <string.h>:

O mesmo raciocínio explica por que o == não compara strings. if (a == b) compara dois endereços, então é falso para dois arrays diferentes guardando texto idêntico. Use strcmp(a, b) == 0 no lugar. As duas funções e suas parentes mais seguras estão em funções de string.

Arrays de strings

Uma lista de strings é ou um array de ponteiros (para texto fixo que você só lê) ou um array bidimensional de char (quando cada entrada precisa ser modificável):

O array de ponteiros guarda três endereços de literais somente leitura - compacto, mas não modificável. A forma char[3][16] reserva 48 bytes da sua própria memória, então cada linha pode ser reescrita.

Erros comuns

  • Esquecer o byte do terminador. char s[5] = "hello" não tem espaço para o '\0'. Dimensione para strlen + 1.
  • Usar sizeof onde você queria strlen. O sizeof num ponteiro dá o tamanho do ponteiro, não o comprimento do texto.
  • Escrever através de um char * para um literal. Declare esses ponteiros como const char *.
  • Comparar com ==. Isso compara endereços. Use strcmp.
  • Ler com scanf("%s", buf). Não há limite de tamanho e o estouro acontece alegremente; prefira fgets.

Perguntas frequentes

Como se declara uma string em C?

Como um array de char: char nome[] = "Ada";. O C não tem tipo string embutido - esse array guarda 'A', 'd', 'a' e um quarto byte oculto, '\0', que marca o fim. Se você quiser espaço para mudar o texto depois, dê ao array um tamanho explícito: char nome[32] = "Ada";.

O que é o terminador nulo em C?

É o byte '\0' (valor zero) que marca onde uma string termina. Toda função padrão - printf("%s"), strlen, strcpy - lê adiante até encontrar esse byte. Um array de char sem ele não é uma string, e passá-lo a essas funções lê além do fim da sua memória.

Por que não posso atribuir uma string com = em C?

Porque um array não é um valor que se possa copiar com =. char a[10]; a = "oi"; não compila. Copie os caracteres com strcpy(a, "oi"), de <string.h>, ou inicialize na declaração - char a[10] = "oi"; - que é o único lugar onde o atalho funciona.

Qual a diferença entre char * e char [] em C?

char s[] = "oi" cria um array modificável com uma cópia privada do texto. char *p = "oi" cria um ponteiro para um literal de string, que mora em memória somente leitura - lê-lo tudo bem, escrever por ele trava. Use o array quando pretende modificar, e o ponteiro quando só vai ler.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR