Menu

Cadenas en C: arrays de char, el terminador nulo y ejemplos

C no tiene tipo cadena: una cadena es un array de char que termina en un byte '\0'. Esta página construye ese modelo mental y luego cubre los literales, la impresión, el recorrido y por qué no puedes copiar una cadena con =.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

La mayoría de los lenguajes te dan un tipo string que conoce su propia longitud. C no. En C una cadena es simplemente un array de char con una regla adjunta: al texto le sigue un byte con valor cero, escrito '\0' y llamado el terminador nulo. Todo lo demás sobre las cadenas de C se deduce de esa única convención.

Una cadena es un array de char más '\0'

Declara una cadena inicializando un array de char a partir de un literal:

El array tiene cuatro bytes, no tres:

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

sizeof(name) es 4. El compilador contó los caracteres, sumó uno para el terminador y dimensionó el array por ti. Ese byte extra es por lo que char name[3] = "Ada"; es un error esperando ocurrir: el texto cabe pero el terminador no, así que nada de lo que venga después puede saber dónde termina la cadena.

Cuando quieras sitio para cambiar el contenido más tarde, da tú el tamaño y deja holgura:

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

Los bytes sin usar los rellena de ceros el inicializador, lo cual es inofensivo.

Por qué importa el terminador

Ninguna función de C recibe una longitud. A printf("%s", s) se le entrega una dirección y avanza imprimiendo bytes hasta que se topa con '\0'. Así que un array de char sin terminador no es una cadena, y tratarlo como tal lee la memoria que dé la casualidad de estar después.

La regla que hay que memorizar: sizeof te da el almacenamiento, strlen te da el texto. Para char buf[32] = "Ada", sizeof buf es 32 y strlen(buf) es 3. Responden preguntas distintas y confundirlas es uno de los errores más comunes de C.

Literales de cadena frente a arrays de char

Estas dos líneas se parecen y se comportan de forma muy distinta:

char  a[] = "hello";   /* un array tuyo, inicializado desde el literal */
char *p   = "hello";   /* un puntero apuntando al literal mismo */

a es un array de 6 bytes con una copia privada del texto. Puedes modificarlo. p apunta dentro de los datos de cadena de solo lectura del programa; el literal se comparte, y escribir a través de p es comportamiento indefinido que típicamente revienta en tiempo de ejecución en lugar de en compilación.

Escribe const char * siempre que un puntero apunte a un literal. El compilador rechaza entonces la escritura en tiempo de compilación en lugar de dejar que se convierta en un fallo. Elegir entre los dos es en realidad una cuestión de punteros y arrays, cubierta en punteros y arrays.

Una diferencia más que conviene conocer: sizeof a es 6, pero sizeof p es el tamaño de un puntero (8 en la mayoría de los sistemas) sin importar cuán largo sea el texto.

Imprimir y leer cadenas

%s imprime una cadena; %c imprime un solo carácter.

Para la entrada, evita gets por completo: se eliminó del lenguaje porque no se puede usar de forma segura. Usa fgets, que toma el tamaño del búfer y no se desbordará:

fgets conserva el salto de línea que pulsaste, que casi nunca es lo que quieres dentro de la cadena. La línea con strcspn encuentra el primer '\n' y lo sobrescribe con un terminador: una forma compacta y estándar de recortarlo.

Recorrer una cadena carácter por carácter

Como el final está marcado en lugar de contado, el bucle idiomático comprueba el carácter mismo:

La condición text[i] != '\0' se abrevia a menudo a solo text[i], ya que '\0' es cero y por tanto falso. Ambas son correctas; la forma explícita se lee más fácil mientras estás aprendiendo.

Puedes recorrerla con un puntero en lugar de un índice, lo cual es igual de idiomático en C:

La conversión a unsigned char antes de llamar a toupper no es decoración: las funciones de <ctype.h> están indefinidas para valores negativos, y un char a secas puede ser negativo para bytes por encima de 127.

No puedes copiar una cadena con =

Este es el muro con el que choca todo recién llegado:

char a[10];
a = "hello";        /* error: assignment to expression with array type */

char b[10] = "hi";
char c[10];
c = b;              /* error, por la misma razon */

El nombre de un array no es un valor modificable, así que = no tiene nada que hacer. El único lugar donde el atajo funciona es la inicialización en la declaración, que es una operación distinta realizada por el compilador.

Para copiar en tiempo de ejecución, copia los bytes con strcpy de <string.h>:

El mismo razonamiento explica por qué == no compara cadenas. if (a == b) compara dos direcciones, así que es falso para dos arrays distintos con texto idéntico. Usa strcmp(a, b) == 0 en su lugar. Ambas funciones y sus parientes más seguras se cubren en funciones de cadenas.

Arrays de cadenas

Una lista de cadenas es o bien un array de punteros (para texto fijo que solo lees) o bien un array bidimensional de char (cuando cada entrada debe ser modificable):

El array de punteros guarda tres direcciones a literales de solo lectura: compacto, pero no modificable. La forma char[3][16] reserva 48 bytes de tu propia memoria, así que cada fila se puede reescribir.

Errores comunes

  • Olvidar el byte del terminador. char s[5] = "hello" no tiene sitio para '\0'. Dimensiona para strlen + 1.
  • Usar sizeof donde querías strlen. sizeof sobre un puntero da el tamaño del puntero, no la longitud del texto.
  • Escribir a través de un char * a un literal. Declara esos punteros como const char *.
  • Comparar con ==. Eso compara direcciones. Usa strcmp.
  • Leer con scanf("%s", buf). No tiene límite de tamaño y se desborda alegremente; prefiere fgets.

Preguntas frecuentes

¿Cómo se declara una cadena en C?

Como un array de char: char name[] = "Ada";. C no tiene un tipo cadena incorporado: ese array contiene 'A', 'd', 'a' y un cuarto byte oculto, '\0', que marca el final. Si quieres sitio para cambiar el texto después, dale al array un tamaño explícito: char name[32] = "Ada";.

¿Qué es el terminador nulo en C?

El byte '\0' (valor cero) que marca dónde termina una cadena. Toda función estándar —printf("%s"), strlen, strcpy— lee hacia adelante hasta encontrar ese byte. Un array de char sin él no es una cadena, y pasárselo a esas funciones lee más allá del final de tu memoria.

¿Por qué no puedo asignar una cadena con = en C?

Porque un array no es un valor que puedas copiar con =. char a[10]; a = "hi"; no compila. Copia los caracteres con strcpy(a, "hi") de <string.h>, o inicializa en la declaración —char a[10] = "hi";— que es el único lugar donde el atajo funciona.

¿Cuál es la diferencia entre char * y char [] en C?

char s[] = "hi" crea un array modificable con una copia privada del texto. char *p = "hi" crea un puntero a un literal de cadena, que vive en memoria de solo lectura: leerlo está bien, escribir a través de él revienta. Usa el array cuando pienses modificar y el puntero cuando solo leas.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR