Estaba leyendo un hilo titulado "strlen vs sizeof" en CodeGuru , y una de las respuestas dice que "de todos modos [sic] es una mala práctica inicializar [sic] una char
matriz con un literal de cadena".
¿Es esto cierto, o es solo su opinión (aunque sea un "miembro de élite")?
Aquí está la pregunta original:
#include <stdio.h>
#include<string.h>
main()
{
char string[] = "october";
strcpy(string, "september");
printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string));
return 0;
}
derecho. el tamaño debe ser la longitud más 1 sí?
esta es la salida
the size of september is 8 and the length is 9
El tamaño debe ser 10 seguramente. es como calcular el tamaño de la cadena antes de que se cambie por strcpy pero la longitud después.
¿Hay algún problema con mi sintaxis o qué?
Aquí está la respuesta :
De todos modos, es una mala práctica inicializar una matriz de caracteres con un literal de cadena. Así que siempre haz uno de los siguientes:
const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
fuente
Respuestas:
El autor de ese comentario nunca lo justifica realmente, y la declaración me parece desconcertante.
En C (y ha etiquetado esto como C), esa es prácticamente la única forma de inicializar una matriz
char
con un valor de cadena (la inicialización es diferente de la asignación). Puedes escribir cualquierao
o
En el primer caso, el tamaño de la matriz se toma del tamaño del inicializador. Los literales de cadena se almacenan como matrices
char
con un byte final de 0, por lo que el tamaño de la matriz es 8 ('o', 'c', 't', 'o', 'b', 'e', 'r', 0). En los segundos dos casos, el tamaño de la matriz se especifica como parte de la declaración (8 yMAX_MONTH_LENGTH
, lo que sea que sea).Lo que no puedes hacer es escribir algo como
o
En el primer caso, la declaración de
string
está incompleta porque no se ha especificado un tamaño de matriz y no hay inicializador para tomar el tamaño. En ambos casos,=
no funcionará porque a) una expresión de matriz tal comostring
puede no ser el objetivo de una asignación yb) el=
operador no está definido para copiar el contenido de una matriz a otra de todos modos.Por esa misma razón, no puedes escribir
donde
foo
es otra matriz dechar
. Esta forma de inicialización solo funcionará con literales de cadena.EDITAR
Debo enmendar esto para decir que también puede inicializar matrices para contener una cadena con un inicializador de estilo de matriz, como
o
pero es más fácil a la vista usar literales de cadena.
EDITAR 2
Para asignar el contenido de una matriz fuera de una declaración, necesitaría usar
strcpy/strncpy
(para cadenas terminadas en 0) omemcpy
(para cualquier otro tipo de matriz):o
fuente
strncpy
rara vez es la respuesta correctachar[8] str = "october";
es una mala práctica. Tuve que literalmente contarme a mí mismo para asegurarme de que no se tratara de un desbordamiento y se rompa bajo mantenimiento ... por ejemplo, corregir un error ortográfico deseprate
aseparate
se romperá si el tamaño no se actualiza.strlen()
no incluir el carácter nulo, utilizandoMAX_MONTH_LENGTH
para mantener el tamaño máximo necesario parachar string[]
menudo se ve . OMI mal,MAX_MONTH_SIZE
sería mejor aquí.El único problema que recuerdo es asignar literal de cadena a
char *
:Por ejemplo, tome este programa:
Esto en mi plataforma (Linux) se bloquea al intentar escribir en la página marcada como de solo lectura. En otras plataformas podría imprimir 'Septiembre', etc.
Dicho esto, la inicialización por literal hace la cantidad específica de reserva para que esto no funcione:
Pero esto lo hará
Como último comentario, no usaría
strcpy
en absoluto:Si bien algunos compiladores pueden cambiarlo a una llamada segura,
strncpy
es mucho más seguro:fuente
strncpy
porque no termina nulo la cadena copiada cuando la longitudsomething_else
es mayor quesizeof(buf)
. Por lo general, configuro el último carácterbuf[sizeof(buf)-1] = 0
para protegerlo, o sibuf
está inicializado en cero, utilizosizeof(buf) - 1
como la longitud de la copia.strlcpy
ostrcpy_s
o inclusosnprintf
si tiene que hacerlo.strlcpy
ysnprintf
no se puede acceder directamente en MSVC, al menos pedidos ystrcpy_s
no en * nix).Una cosa que ninguno de los hilos menciona es esto:
vs.
El primero hará algo como:
Este último solo hace la memoria. El estándar C insiste en que si se inicializa alguna parte de una matriz, todo lo es. Entonces, en este caso, es mejor hacerlo usted mismo. Creo que eso fue a lo que se refería Treuss.
Sin lugar a duda
es mejor que cualquiera:
o
ps Para puntos de bonificación, puede hacer:
para generar un error de división de tiempo de compilación por cero si está a punto de desbordar la matriz.
fuente
Principalmente porque no tendrá el tamaño de
char[]
una variable / construcción que pueda usar fácilmente dentro del programa.El código de muestra del enlace:
string
se asigna en la pila con 7 u 8 caracteres de longitud. No puedo recordar si está terminada en nulo de esta manera o no: el hilo al que se vinculó indicó que sí.Copiar "septiembre" sobre esa cadena es un desbordamiento obvio de memoria.
Otro desafío surge si pasa
string
a otra función para que la otra función pueda escribir en la matriz. Debe decirle a la otra función cuánto dura la matriz para que no cree un desbordamiento. Podría pasarstring
junto con el resultado de,strlen()
pero el hilo explica cómo esto puede explotar sistring
no se termina con nulo.Es mejor asignar una cadena con un tamaño fijo (preferiblemente definido como una constante) y luego pasar la matriz y el tamaño fijo a la otra función. Los comentarios de @John Bode son correctos, y hay formas de mitigar estos riesgos. También requieren más esfuerzo de su parte para usarlos.
En mi experiencia, el valor que inicialicé
char[]
es generalmente demasiado pequeño para los otros valores que necesito colocar allí. El uso de una constante definida ayuda a evitar ese problema.sizeof string
le dará el tamaño del búfer (8 bytes); use el resultado de esa expresión en lugar destrlen
cuando le preocupa la memoria.Del mismo modo, se puede realizar una prueba antes de la llamada a
strcpy
para ver si su memoria intermedia de destino es lo suficientemente grande para la cadena de origen:if (sizeof target > strlen(src)) { strcpy (target, src); }
.Sí, si usted tiene que pasar la matriz a una función, tendrá que pasar su tamaño físico, así:
foo (array, sizeof array / sizeof *array);
. - John Bodefuente
sizeof string
le dará el tamaño del búfer (8 bytes); use el resultado de esa expresión en lugar destrlen
cuando le preocupa la memoria. Del mismo modo, se puede realizar una prueba antes de la llamada astrcpy
para ver si su memoria intermedia de destino es lo suficientemente grande para la cadena de origen:if (sizeof target > strlen(src)) { strcpy (target, src); }
. Sí, si usted tiene que pasar la matriz a una función, tendrá que pasar su tamaño físico, así:foo (array, sizeof array / sizeof *array);
.string
resultan en una conversión implícita achar*
, apuntando al primer elemento de la matriz. Esto pierde la información de los límites de la matriz. Una llamada de función es solo uno de los muchos contextos en los que esto sucede.char *ptr = string;
es otro. Evenstring[0]
es un ejemplo de esto; el[]
operador trabaja en punteros, no directamente en matrices. Lectura recomendada: Sección 6 de la FAQ comp.lang.c .Creo que la idea de "mala práctica" proviene del hecho de que esta forma:
hace implícitamente un strcpy del código fuente de la máquina a la pila.
Es más eficiente manejar solo un enlace a esa cadena. Me gusta con:
o directamente:
(pero, por supuesto, en la mayoría de los códigos probablemente no importa)
fuente
char time_buf[] = "00:00";
donde vas a modificar un búfer? Unchar *
inicializado en un literal de cadena se establece en la dirección del primer byte, por lo que intentar modificarlo da como resultado un comportamiento indefinido porque el método de almacenamiento del literal de cadena es desconocido (implementación definida), mientras que modificar los bytes de achar[]
es perfectamente legal porque la inicialización copia los bytes en un espacio grabable asignado en la pila. Decir que es "menos eficiente" o "mala práctica" sin dar detalles sobre los maticeschar* vs char[]
es engañoso.Nunca es mucho tiempo, pero debe evitar la inicialización char [] a string, porque "string" es const char * y lo está asignando a char *. Entonces, si pasa este char [] al método que cambia los datos, puede tener un comportamiento interesante.
Como dijo el commend, mezclé un poco char [] con char *, eso no es bueno ya que difieren un poco.
No hay nada de malo en asignar datos a la matriz de caracteres, pero como la intención de usar esta matriz es usarla como 'cadena' (char *), es fácil olvidar que no debe modificar esta matriz.
fuente
const
menos que lo defina de esa manera. (Y los literales de cadena en C no lo sonconst
, aunque cualquier intento de modificar un literal de cadena tiene un comportamiento indefinido).char *s = "literal";
Tiene el tipo de comportamiento del que está hablando; está mejor escrito comoconst char *s = "literal";
const
encendidoint n = 42;
, porque42
es una constante.c
es modificable. Es exactamente una garantía tan fuerte como la que1 + 1
evalúa2
. Si el programa al que me vinculé anteriormente hace algo más que imprimirEFGH
, indica una implementación de C no conforme.