resueltos - ¿Están los literales de las cuerdas const?
estadistica elemental solucionario (8)
La respuesta de Johannes es correcta con respecto al tipo y contenido. Pero además de eso, sí, es un comportamiento indefinido modificar los contenidos de un literal de cadena.
Con respecto a tu pregunta sobre argv :
Los parámetros argc y argv y las cadenas apuntadas por la matriz argv serán modificables por el programa y conservarán sus últimos valores almacenados entre el inicio del programa y la terminación del programa.
Tanto GCC como Clang no se quejan si asigno un literal de cadena a un char* , incluso cuando se usan muchas opciones pedantes ( -Wall -W -pedantic -std=c99 ):
char *foo = "bar";
mientras ellos (por supuesto) se quejan si asigno un const char* a un char* .
¿Esto significa que los literales de cadena se consideran de tipo char* ? ¿No deberían ser const char* ? ¡No se trata de un comportamiento definido si se modifican!
Y (una pregunta no correlacionada) ¿qué pasa con los parámetros de línea de comandos (es decir: argv ): se considera que es una matriz de literales de cadena?
Los literales de cadena tienen el tipo formal char [] pero el tipo semántico es const char [] . Los puristas lo odian, pero esto generalmente es útil e inofensivo, a excepción de traer muchos novatos al SO con "¿POR QUÉ ESTÁ CRUZANDO MI PROGRAMA?!?!" preguntas
Son const char *, pero hay una exclusión específica para asignarlos a char * para código heredado que existía antes de const did. Y los argumentos de línea de comandos definitivamente no son literales, se crean en tiempo de ejecución.
Son del tipo char[N] donde N es el número de caracteres, incluido el /0 finalizador. Entonces sí, puedes asignarlos a char* , pero aún no puedes escribirles (el efecto no estará definido).
Wrt argv : apunta a una serie de punteros a cadenas. Esas cadenas son explícitamente modificables. Puede cambiarlos y se requiere que contengan el último valor almacenado.
Tanto en C89 como en C99, los literales de cadena son de tipo char * (por razones históricas, según lo entiendo). Estás en lo correcto al tratar de modificar uno de los resultados en un comportamiento indefinido. GCC tiene una bandera de advertencia específica, -Wwrite-strings (que no es parte de -Wall ), que te avisará si intentas hacerlo.
En cuanto a argv , los argumentos se copian en el espacio de direcciones de su programa, y se pueden modificar de forma segura en su función main() .
EDITAR : Whoops, tenía -Wno-write-strings copiados por accidente. Actualizado con la forma correcta (positiva) de la bandera de advertencia.
(Lo siento, acabo de notar que esta pregunta está etiquetada como c , no c++ . ¡Tal vez mi respuesta no es tan relevante para esta pregunta después de todo!)
Los literales de cadena no son const o not-const , existe una regla extraña especial para los literales.
( Resumen : Literales se pueden tomar por referencia-a-matriz como foo( const char (&)[N]) y no se pueden tomar como la matriz no const. Ellos prefieren decaer a const char * . Hasta ahora, eso hace parece que son const . Pero existe una regla de legado especial que permite que los literales se descompongan en char * . Consulte los experimentos a continuación).
(Después de los experimentos realizados en clang3.3 con -std=gnu++0x . ¿Quizás esto sea un problema de C ++ 11? ¿O específico para clang? De cualquier forma, hay algo extraño en curso).
Al principio, los literales parecen ser const :
void foo( const char * ) { std::cout << "const char *" << std::endl; }
void foo( char * ) { std::cout << " char *" << std::endl; }
int main() {
const char arr_cc[3] = "hi";
char arr_c[3] = "hi";
foo(arr_cc); // const char *
foo(arr_c); // char *
foo("hi"); // const char *
}
Las dos matrices se comportan como se espera, lo que demuestra que foo puede decirnos si el puntero es const o no. Luego "hi" selecciona la versión const de foo . Entonces parece que eso lo soluciona: los literales son const ... ¿no?
Pero , si elimina void foo( const char * ) entonces se vuelve extraño. Primero, la llamada a foo(arr_c) falla con un error en tiempo de compilación. Eso es lo que se espera Pero la llamada literal ( foo("hi") ) funciona a través de la llamada non-const.
Entonces, los literales son "más const" que arr_c (porque prefieren decaer al const char * , a diferencia de arr_c . Pero los literales son "menos const" que arr_cc porque están dispuestos a decaer a char * si es necesario.
(Clang da una advertencia cuando se descompone en char * ).
Pero, ¿y el decaimiento? Evitemos esto por simplicidad.
Tomemos las matrices por referencia en foo en su lugar. Esto nos da resultados más ''intuitivos'':
void foo( const char (&)[3] ) { std::cout << "const char (&)[3]" << std::endl; }
void foo( char (&)[3] ) { std::cout << " char (&)[3]" << std::endl; }
Como antes, el literal y la matriz const ( arr_cc ) utilizan la versión const, y la versión no const es utilizada por arr_c . Y si eliminamos foo( const char (&)[3] ) , entonces obtenemos errores con ambos foo(arr_cc); y foo("hi"); . En resumen, si evitamos el puntero-decaimiento y usamos reference-to-array, los literales se comportan como si fueran const .
¿Plantillas?
En las plantillas, el sistema deducirá const char * lugar de char * y usted quedará "atrapado" con eso.
template<typename T>
void bar(T *t) { // will deduce const char when a literal is supplied
foo(t);
}
Así que, básicamente, un literal se comporta como const en todo momento, excepto en el caso particular en que se inicializa directamente un char * con un literal.
Para mayor información, el borrador del estándar C99 ( C89 y C11 tienen una redacción similar ) en la sección 6.4.5 cadenas, el párrafo 5 dice:
[...] un byte o código de valor cero se agrega a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales. La secuencia de caracteres multibyte se usa luego para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia. Para los literales de cadena de caracteres, los elementos de la matriz tienen tipo char , y se inicializan con los bytes individuales de la secuencia de caracteres multibyte; [...]
Así que esto dice que un literal de cadena tiene una duración de almacenamiento estática ( dura el tiempo de vida del programa ) y su tipo es char[] (no char * ) y su longitud es el tamaño del literal de cadena con un cero adjunto. * El párrafo 6` dice:
Si el programa intenta modificar dicha matriz, el comportamiento no está definido.
Por lo tanto, intentar modificar una cadena literal es un comportamiento indefinido independientemente del hecho de que no sean const .
Con respecto a argv en la sección 5.1.2.2.1 el párrafo 2 del inicio del programa dice:
Si se declaran, los parámetros de la función principal obedecerán las siguientes restricciones:
[...]
-Los parámetros argc y argv y las cadenas apuntadas por la matriz argv serán modificables por el programa y conservarán sus últimos valores almacenados entre el inicio del programa y la terminación del programa.
Entonces argv no se considera una matriz de literales de cadenas y está bien modificar los contenidos de argv .
Usando la opción -Wwrite-strings obtendrás:
warning: initialization discards qualifiers from pointer target type
Independientemente de esa opción, GCC colocará los literales en la sección de memoria de solo lectura, a menos que se indique lo contrario utilizando -fwritable-strings (sin embargo, esta opción se ha eliminado de las versiones recientes de GCC).
Los parámetros de línea de comando no son const, por lo general viven en la pila.