¿Cómo se deben utilizar las matrices de caracteres como cadenas?

Resuelto Lundin asked hace 5 años • 4 respuestas

Entiendo que las cadenas en C son solo matrices de caracteres. Así que probé el siguiente código, pero da resultados extraños, como resultados basura o fallas del programa:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

¿Por qué esto no funciona?

Se compila limpiamente con gcc -std=c17 -pedantic-errors -Wall -Wextra.


Nota: Esta publicación está destinada a usarse como preguntas frecuentes canónicas para problemas derivados de no asignar espacio para un terminador NUL al declarar una cadena.

Lundin avatar Oct 23 '19 22:10 Lundin
Aceptado

La cadena AC es una matriz de caracteres que termina con un terminador nulo .

Todos los caracteres tienen un valor de tabla de símbolos. El terminador nulo es el valor del símbolo 0(cero). Se utiliza para marcar el final de una cuerda. Esto es necesario ya que el tamaño de la cadena no se almacena en ningún lugar.

Por lo tanto, cada vez que asigne espacio para una cadena, debe incluir espacio suficiente para el carácter terminador nulo. Su ejemplo no hace esto, solo asigna espacio para los 5 caracteres de "hello". El código correcto debe ser:

char str[6] = "hello";

O de manera equivalente, puede escribir código autodocumentado para 5 caracteres más 1 terminador nulo:

char str[5+1] = "hello";

Pero también puedes usar esto y dejar que el compilador cuente y elija el tamaño:

char str[] = "hello"; // Will allocate 6 bytes automatically

Al asignar memoria para una cadena dinámicamente en tiempo de ejecución, también es necesario asignar espacio para el terminador nulo:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

Si no agrega un terminador nulo al final de una cadena, las funciones de la biblioteca que esperan una cadena no funcionarán correctamente y obtendrá errores de "comportamiento indefinido", como salida basura o fallas del programa.

La forma más común de escribir un carácter terminador nulo en C es mediante el uso de la llamada "secuencia de escape octal", con este aspecto: '\0'. Esto es 100% equivalente a escribir 0, pero \sirve como código autodocumentado para indicar que el cero está destinado explícitamente a ser un terminador nulo. Un código como el que if(str[i] == '\0')comprobará si el carácter específico es el terminador nulo.

Tenga en cuenta que el término terminador nulo no tiene nada que ver con los punteros nulos o la NULLmacro. Esto puede resultar confuso: nombres muy similares pero significados muy diferentes. Esta es la razón por la que a veces se hace referencia al terminador nulo NULcon una L, que no debe confundirse con NULLpunteros nulos. Consulte las respuestas a esta pregunta SO para obtener más detalles.

En "hello"su código se llama cadena literal . Esto debe considerarse como una cadena de sólo lectura. La ""sintaxis significa que el compilador agregará automáticamente un terminador nulo al final del literal de cadena. Entonces, si imprime, sizeof("hello")obtendrá 6, no 5, porque obtiene el tamaño de la matriz, incluido un terminador nulo.


Se compila limpiamente con gcc.

De hecho, ni siquiera una advertencia. Esto se debe a un detalle/fallo sutil en el lenguaje C que permite inicializar matrices de caracteres con una cadena literal que contiene exactamente tantos caracteres como espacio hay en la matriz y luego descartar silenciosamente el terminador nulo (C17 6.7.9/ 15). El lenguaje se comporta así deliberadamente por razones históricas; consulte Diagnóstico gcc inconsistente para la inicialización de cadenas para obtener más detalles. También tenga en cuenta que C++ es diferente aquí y no permite el uso de este truco/defecto.

Lundin avatar Oct 23 '2019 15:10 Lundin

Del Estándar C (7.1.1 Definiciones de términos)

1 Una cadena es una secuencia contigua de caracteres que termina en el primer carácter nulo (incluido). El término cadena multibyte se utiliza a veces para enfatizar el procesamiento especial dado a los caracteres multibyte contenidos en la cadena o para evitar confusión con una cadena ancha. Un puntero a una cadena es un puntero a su carácter inicial (con la dirección más baja). La longitud de una cadena es el número de bytes que preceden al carácter nulo y el valor de una cadena es la secuencia de los valores de los caracteres contenidos, en orden.

En esta declaración

char str [5] = "hello";

el literal de cadena "hello"tiene la representación interna como

{ 'h', 'e', 'l', 'l', 'o', '\0' }

por lo que tiene 6 caracteres, incluido el cero final. Sus elementos se utilizan para inicializar la matriz de caracteres strque reserva espacio solo para 5 caracteres.

El estándar C (a diferencia del estándar C++) permite dicha inicialización de una matriz de caracteres cuando el cero final de una cadena literal no se utiliza como inicializador.

Sin embargo, como resultado, la matriz de caracteres strno contiene una cadena.

Si desea que la matriz contenga una cadena, puede escribir

char str [6] = "hello";

o solo

char str [] = "hello";

En el último caso, el tamaño de la matriz de caracteres se determina a partir del número de inicializadores del literal de cadena que es igual a 6.

Vlad from Moscow avatar Oct 23 '2019 15:10 Vlad from Moscow