"Vida útil" de un literal de cadena en C

Resuelto user113454 asked hace 12 años • 9 respuestas

¿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?

user113454 avatar Apr 02 '12 09:04 user113454
Aceptado

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 constwhile 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 constaunque no en C. Sin embargo, declarar una cadena literal con a constle 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 .

Alok Save avatar Apr 02 '2012 02:04 Alok Save

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 un char16_tliteral de cadena. Un char16_tliteral de cadena tiene el tipo "matriz de n const 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 un char16_tcarácter en forma de pares sustitutos.

10 Un literal de cadena que comienza con U, como U"asdf", es un char32_tliteral de cadena. Un char32_tliteral de cadena tiene el tipo "matriz de n const 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 n const 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.

Daniel Fischer avatar May 09 '2013 20:05 Daniel Fischer

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 fooaconst char *foo(int)

asaelr avatar Apr 02 '2012 02:04 asaelr