"Vida útil" de un literal de cadena en C
¿No sería inaccesible el puntero devuelto por la siguiente función?
char *foo(int rc)
{
switch (rc)
{
case 1:
return("one");
case 2:
return("two");
default:
return("whatever");
}
}
Entonces, la vida útil de una variable local en C/C++ es prácticamente solo dentro de la función, ¿verdad? Lo que significa que, despuéschar* foo(int)
terminar, el puntero que devuelve ya no significa nada, ¿verdad?
Estoy un poco confundido acerca de la vida útil de una variable local. ¿Qué es una buena aclaración?
Sí, la vida útil de una variable local está dentro del alcance ( {
, }
) en el que se crea.
Las variables locales tienen almacenamiento automático o local. Automático porque se destruyen automáticamente una vez finaliza el ámbito dentro del cual fueron creados.
Sin embargo, lo que tiene aquí es una cadena literal, que se asigna en una memoria de solo lectura definida por la implementación. Los literales de cadena son diferentes de las variables locales y permanecen vivos durante toda la vida del programa. Tienen duración estática [Ref 1] de por vida.
¡Una palabra de precaución!
Sin embargo, tenga en cuenta que cualquier intento de modificar el contenido de una cadena literal es un comportamiento indefinido (UB). Los programas de usuario no pueden modificar el contenido de una cadena literal.
Por lo tanto, siempre se recomienda utilizar un const
while para declarar una cadena literal.
const char*p = "string";
en lugar de,
char*p = "string";
De hecho, en C++ está en desuso declarar una cadena literal sin el const
aunque no en C. Sin embargo, declarar una cadena literal con a const
le da la ventaja de que los compiladores generalmente le darán una advertencia en caso de que intente modificar la cadena literal en el segundo caso.
Programa de muestra :
#include<string.h>
int main()
{
char *str1 = "string Literal";
const char *str2 = "string Literal";
char source[]="Sample string";
strcpy(str1,source); // No warning or error just Undefined Behavior
strcpy(str2,source); // Compiler issues a warning
return 0;
}
Producción:
cc1: las advertencias se tratan como errores
prog.c: en la función 'principal':
prog.c:9: error: pasar el argumento 1 de 'strcpy' descarta los calificadores del tipo de destino del puntero
Observe que el compilador advierte para el segundo caso, pero no para el primero.
Para responder a la pregunta que hacen un par de usuarios aquí:
¿Cuál es el problema con los literales integrales?
En otras palabras, ¿es válido el siguiente código?
int *foo()
{
return &(2);
}
La respuesta es no, este código no es válido. Está mal formado y dará un error de compilación.
Algo como:
prog.c:3: error: lvalue required as unary ‘&’ operand
Los literales de cadena son valores l, es decir: puede tomar la dirección de un literal de cadena, pero no puede cambiar su contenido.
Sin embargo, cualquier otro literal ( int
, float
, char
, etc.) son valores r (el estándar C usa el término valor de una expresión para estos) y su dirección no se puede tomar en absoluto.
[Ref 1] Estándar C99 6.4.5/5 "Literales de cadena - Semántica":
En la fase de traducción 7, se agrega un byte o código de valor cero a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales. La secuencia de caracteres multibyte se utiliza luego para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia . Para los literales de cadenas de caracteres, los elementos de la matriz tienen el tipo char y se inicializan con los bytes individuales de la secuencia de caracteres multibyte; para literales de cadena ancha, los elementos de la matriz tienen el tipo wchar_t y se inicializan con la secuencia de caracteres anchos...
No se especifica si estas matrices son distintas siempre que sus elementos tengan los valores apropiados. Si el programa intenta modificar dicha matriz, el comportamiento no está definido .
Es válido. Los literales de cadena tienen una duración de almacenamiento estático, por lo que el puntero no cuelga.
Para C, esto se exige en la sección 6.4.5, párrafo 6:
En la fase de traducción 7, se agrega un byte o código de valor cero a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales. Luego, la secuencia de caracteres multibyte se utiliza para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia.
Y para C++ en la sección 2.14.5, párrafos 8-11:
8 Los literales de cadena ordinarios y los literales de cadena UTF-8 también se denominan literales de cadena estrechos. Un literal de cadena estrecha tiene el tipo "matriz de n
const char
", donde n es el tamaño de la cadena como se define a continuación, y tiene una duración de almacenamiento estático (3.7).9 Un literal de cadena que comienza con u, como
u"asdf"
, es unchar16_t
literal de cadena. Unchar16_t
literal de cadena tiene el tipo "matriz de nconst char16_t
", donde n es el tamaño de la cadena como se define a continuación; tiene una duración de almacenamiento estático y se inicializa con los caracteres dados. Un único carácter c puede producir más de unchar16_t
carácter en forma de pares sustitutos.10 Un literal de cadena que comienza con U, como
U"asdf"
, es unchar32_t
literal de cadena. Unchar32_t
literal de cadena tiene el tipo "matriz de nconst char32_t
", donde n es el tamaño de la cadena como se define a continuación; tiene una duración de almacenamiento estático y se inicializa con los caracteres dados.11 Una cadena literal que comienza con L, como
L"asdf"
, es una cadena literal ancha. Un literal de cadena ancha tiene el tipo "matriz de nconst wchar_t
", donde n es el tamaño de la cadena como se define a continuación; tiene una duración de almacenamiento estático y se inicializa con los caracteres dados.
Los literales de cadena son válidos para todo el programa (y no se asignan a la pila), por lo que serán válidos.
Además, los literales de cadena son de sólo lectura, por lo que (para un buen estilo) tal vez deberías cambiar foo
aconst char *foo(int)