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

9 minutos de lectura

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

#include <stdio.h>

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

¿Por qué no funciona esto?

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 una falla en la asignación de espacio para un terminador NUL al declarar una cadena.

todos pueden instrumentos de cuerda ser considerado un variedad de personajes (), pueden todos matrices de caracteres ser considerado instrumentos de cuerda (No).

¿Por qué no? y ¿Por qué importa?

Además de las otras respuestas que explican que la longitud de una cadena no se almacena en ningún lugar como parte de la cadena y las referencias al estándar donde se define una cadena, la otra cara es “¿Cómo manejan las cadenas las funciones de la biblioteca C?”

Si bien una matriz de caracteres puede contener los mismos caracteres, es simplemente una matriz de caracteres a menos que el último carácter vaya seguido del terminación nula personaje. Ese terminación nula El carácter es lo que permite que la matriz de caracteres se considere (maneje como) una cadena.

Todas las funciones en C que esperan una cadena como argumento esperan que la secuencia de caracteres sea terminado en nulo. ¿Por qué?

Tiene que ver con la forma en que funcionan todas las funciones de cadena. Dado que la longitud no se incluye como parte de una matriz, las funciones de cadena exploran hacia adelante en la matriz hasta que la carácter nulo (p.ej '\0' — equivalente a decimal 0) es encontrado. Ver Tabla ASCII y descripción. Independientemente de si está utilizando strcpy, strchr, strcspnetc. Todas las funciones de cadena se basan en el terminación nula carácter que está presente para definir dónde está el final de esa cadena.

Una comparación de dos funciones similares de string.h enfatizará la importancia de la terminación nula personaje. Toma por ejemplo:

    char *strcpy(char *dest, const char *src);

Él strcpy función simplemente copia bytes de src para dest hasta el terminación nula el personaje se encuentra diciendo strcpy dónde dejar de copiar caracteres. Ahora toma la función similar memcpy:

    void *memcpy(void *dest, const void *src, size_t n);

La función realiza una operación similar, pero no considera ni requiere la src parámetro para ser una cadena. Ya que memcpy no puede simplemente escanear hacia adelante en src copiando bytes a dest hasta un terminación nula se alcanza el carácter, requiere un número explícito de bytes para copiar como tercer parámetro. Este tercer parámetro proporciona memcpy con la misma información de tamaño strcpy es capaz de derivar simplemente escaneando hacia adelante hasta que un terminación nula se encuentra el personaje.

(que también enfatiza lo que va mal en strcpy (o cualquier función que espere una cadena) si no proporciona a la función un terminado en nulo cadena: no tiene idea de dónde detenerse y felizmente correrá por el resto de su segmento de memoria invocando Comportamiento indefinido hasta un carácter nulo simplemente se encuentra en algún lugar de la memoria, o se produce una falla de segmentación)

Es decir por qué funciones que esperan un terminado en nulo la cadena debe ser pasada a terminado en nulo cuerda y por qué importa.

  • Me gusta esta respuesta, ya que presenta la idea de una capa adicional de semántica en las cadenas. una matriz de char es solo eso Ahora, puedo definir una cadena para que sea “una matriz de caracteres, el último de los cuales tiene valor 0”. Pero también podría definirlo de otra manera (por ejemplo: “una serie de caracteres, el primero de los cuales indica cuántos siguen” – también conocido como cuerdas de pascal). No es que una cadena deba terminar en 0, sino que la biblioteca C define la palabra cadena para que tenga ese significado.

    – espectros

    11 de noviembre de 2020 a las 14:46

  • Sí, la semántica es C (y .c_str() en C++), cadenas de Pascal o conceptos en otros lenguajes hacen las cosas de manera diferente. El objetivo es el mismo. Imagine una cadena de Pascal a la que le falta la información de longitud :)

    –David C. Rankin

    11 de noviembre de 2020 a las 15:29

AC string 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 cadena. Esto es necesario ya que el tamaño de la cadena no se almacena en ninguna parte.

Por lo tanto, cada vez que asigne espacio para una cadena, debe incluir suficiente espacio para el carácter de terminación nulo. Su ejemplo no hace esto, solo asigna espacio para los 5 caracteres de "hello". El código correcto debería 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 puede 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 debe 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 la salida de basura o fallas del programa.

La forma más común de escribir un carácter terminador nulo en C es usando la llamada “secuencia de escape octal”, que se ve así: '\0'. Esto es 100% equivalente a escribir 0pero el \ sirve como código de autodocumentación para indicar que el cero está destinado explícitamente a ser un terminador nulo. Código como 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 punteros nulos o el NULL ¡macro! Esto puede ser confuso: nombres muy similares pero significados muy diferentes. Esta es la razón por la cual el terminador nulo a veces se denomina NUL con una L, que no debe confundirse con NULL o punteros nulos. Consulte las respuestas a esta pregunta SO para obtener más detalles.

Él "hello" en tu código se llama literal de cadena. Esto se debe considerar como una cadena de solo lectura. Él "" La sintaxis significa que el compilador agregará automáticamente un terminador nulo al final de la cadena literal. Así que si imprimes 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/falla sutil en el lenguaje C que permite que las matrices de caracteres se inicialicen con una cadena literal que contiene exactamente tantos caracteres como espacio haya en la matriz y luego descartar silenciosamente el terminador nulo (C17 6.7.9/ 15). El lenguaje se está comportando así a propósito por razones históricas, consulte Diagnóstico de gcc inconsistente para la inicialización de cadenas para obtener más información. También tenga en cuenta que C ++ es diferente aquí y no permite que se use este truco/defecto.

  • … y tal vez también el char *str = "hello";str[0] = foo; problema.

    – Jabberwocky

    23 oct 2019 a las 15:25

  • Tal vez extienda la implicación de usar sizeof a su uso en un parámetro de función, especialmente cuando se define como una matriz.

    – Veleta

    23 oct 2019 a las 15:35

  • @WeatherVane Debería estar cubierto por otra pregunta frecuente aquí: stackoverflow.com/questions/492384/…

    – Lundin

    23 oct 2019 a las 15:37

  • Una cadena c es (parte de) una matriz de caracteres en la que uno de los elementos (no necesariamente el último) es 0 … o, según el contexto, un puntero a un elemento de una matriz de caracteres que contiene un 0 en un elemento posterior.

    – pmg

    23 oct 2019 a las 15:42

  • he estado usando el char[n+1] paradigma de proporcionar explícitamente espacio para el terminador nulo dentro de las declaraciones de cadena durante décadas. Es un verdadero ejemplo de ‘código autodocumentado’.

    – David R. Tribble

    23 oct 2019 a las 16:35


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

1 Una cadena es una secuencia contigua de caracteres que termina e incluye el primer carácter nulo. El término cadena de varios bytes se usa a veces para enfatizar el procesamiento especial dado a los caracteres de varios bytes contenidos en la cadena o para evitar confusiones con una cadena ancha. Un puntero a una cadena es un puntero a su carácter inicial (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";

la cadena literal "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. str que reservan espacio solo para 5 caracteres.

El Estándar C (opuesto al Estándar C++) permite tal inicialización de una matriz de caracteres cuando el cero final de un literal de cadena no se usa como inicializador.

Sin embargo, como resultado, la matriz de caracteres str no 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.

Intuitivamente…

Piense en una matriz como una variable (contiene cosas) y una cadena como un valor (se puede colocar en una variable).

Ciertamente no son lo mismo. En su caso, la variable es demasiado pequeña para contener la cadena, por lo que la cadena se corta. (“las cadenas entre comillas” en C tienen un carácter nulo implícito al final).

Sin embargo, es posible almacenar una cadena en una matriz que es mucho más grande que la cuerda.

Tenga en cuenta que los operadores habituales de asignación y comparación (= == < etc.) no funcionan como cabría esperar. Pero el strxyz familia de funciones se acerca bastante, una vez que sabes lo que estás haciendo. Ver el Preguntas frecuentes sobre instrumentos de cuerda y arreglos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad