solucionario resueltos probabilidad estadistica elemental ejercicios c string constants const literals

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.