lenguaje - "Tiempo de vida" de cadena literal en C
imprimir cadena de caracteres en c (9)
Buena pregunta. En general, tendrías razón, pero tu ejemplo es la excepción. El compilador asigna de forma estática memoria global para un literal de cadena. Por lo tanto, la dirección devuelta por su función es válida.
Que esto es así es una característica bastante conveniente de C, ¿no es así? Permite que una función devuelva un mensaje precompuesto sin forzar al programador a preocuparse por la memoria en la que se almacena el mensaje.
Ver también la observación correcta de @asaelr con const
.
¿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, una vez que termina char* foo(int)
, el puntero que devuelve ya no significa nada.
Estoy un poco confundido acerca de la vida de la var. ¿Alguien podría darme una buena aclaración?
En el ejemplo anterior que usted muestra, en realidad está devolviendo los punteros asignados a cualquier función que llame a los anteriores. Entonces no se convertiría en un puntero local. Y, además, los punteros que se necesitan devolver, la memoria se asigna en el segmento global.
Agradeciendote,
Viharri PL V.
Es válido, los literales de cadena tienen una duración de almacenamiento estática, por lo que el puntero no está colgando.
Para C, eso es obligatorio 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 literal. 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.
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 conocen como literales de cadena estrecha. 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 literal de cadenachar16_t
. Un literal de cadenachar16_t
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ática y se inicializa con los caracteres especificados. Un solo c-char puede producir más de un carácterchar16_t
en forma de pares de sustitución.10 Un literal de cadena que comienza con U, como
U"asdf"
, es un literal de cadenachar32_t
. Un literal de cadenachar32_t
tiene el tipo "array of 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ática y se inicializa con los caracteres especificados.11 Un literal de cadena que comienza con L, como
L"asdf"
, es un literal de cadena ancha. Un literal de cadena ancha tiene el tipo "array of 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ática y se inicializa con los caracteres especificados.
Las variables locales solo son válidas dentro del alcance que se declaran, sin embargo, no se declaran variables locales en esa función.
Es perfectamente válido devolver un puntero a un literal de cadena desde una función, ya que existe un literal de cadena a lo largo de toda la ejecución del programa, tal como lo haría una variable static
o una variable global.
Si te preocupa que lo que estás haciendo sea indefinido, deberías subir las advertencias del compilador para ver si hay algo que estás haciendo mal.
Los literales de cadena son válidos para todo el programa (y no están asignados, no la pila), por lo que serán válidos.
Además, los literales de cadena son de solo lectura, así que (para un buen estilo) tal vez deberías cambiar foo
por const char *foo(int)
Sí, es un código válido, caso 1 a continuación. Puede devolver C cadenas de una función de forma segura al menos de estas formas:
const char*
a una cadena literal. No puede ser modificado, no debe ser liberado por la persona que llama. Rara vez es útil para devolver un valor predeterminado, debido al problema de liberación que se describe a continuación. Puede tener sentido si realmente necesita pasar un puntero a la función en alguna parte, por lo que necesita una función que devuelva una cadena.char*
oconst char*
al búfer de char estático. No debe ser liberado por la persona que llama. Puede ser modificado (ya sea por el que llama si no const, o por la función que lo devuelve), pero una función que devuelve esto no puede (fácilmente) tener varios búferes, por lo que (fácilmente) no es seguro y la persona que llama puede necesitar copiar el valor antes de llamar a la función de nuevo.char*
a un buffer asignado conmalloc
. Se puede modificar, pero generalmente debe ser explícitamente liberado por la persona que llama, y tiene la sobrecarga de asignación del montón.strdup
es de este tipo.const char*
ochar*
a un búfer, que se pasó como un argumento a la función (el puntero devuelto no necesita apuntar al primer elemento del búfer de argumentos). Deja la responsabilidad de la administración del búfer / memoria a la persona que llama. Muchas funciones de cadena estándar son de este tipo.
Un problema es que mezclar estos en una función puede complicarse. La persona que llama necesita saber cómo manejar el puntero devuelto, cuánto tiempo es válido y si la persona que llama debería liberarlo, y no hay una forma (buena) de determinarlo en el tiempo de ejecución. Por lo tanto, no puede tener una función, que a veces devuelve un puntero a un búfer asignado por el montón que el llamante necesita free
, y en ocasiones un puntero a un valor predeterminado del literal de cadena, que el llamador no debe free
.
Sí, la duración 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 que termina el alcance dentro del cual se crearon.
Sin embargo, lo que tiene aquí es un literal de cadena, que se asigna en una memoria de solo lectura definida de implementación. Los literales de cadena son diferentes de las variables locales y permanecen vivos durante toda la vida del programa. Tienen una duración de vida estática [Ref 1] .
¡Una palabra de precaución!
Sin embargo, tenga en cuenta que cualquier intento de modificar el contenido de un literal de cadena es un Comportamiento Indefinido. Los programas de usuario no pueden modificar el contenido de un literal de cadena.
Por lo tanto, siempre se recomienda usar un const
al declarar una cadena literal.
const char*p = "string";
en lugar de,
char*p = "string";
De hecho, en C ++ está obsoleto para declarar una cadena literal sin la const
aunque no en c. Sin embargo, declarar una cadena literal con un const
le da la ventaja de que los compiladores generalmente le darían una advertencia en caso de que intente modificar el literal de la cadena en el segundo caso.
#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 Uundefined Behavior
strcpy(str2,source); //Compiler issues a warning
return 0;
}
Salida:
cc1: advertencias que 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 objetivo de puntero
Observe que el compilador advierte para el segundo caso pero no para el primero.
EDITAR: Para responder a la pregunta de Q por un par de usuarios aquí:
¿Cuál es el trato con literales integrales?
En otras palabras, este código es válido:
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 l-values, 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 r-values (el estándar c usa el término el valor de una expresión para estos) y su dirección no puede tomarse en absoluto.
[Ref 1] C99 estándar 6.4.5 / 5 "Literales de cuerda - 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 literal. 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 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; para literales de cadenas anchas, 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 adecuados. Si el programa intenta modificar dicha matriz, el comportamiento no está definido .
Una variable local se asigna en la pila. Una vez que la función finaliza, la variable queda fuera del alcance y ya no es accesible en el código. Sin embargo, si tiene un puntero global (o simplemente no está fuera del alcance) que asignó para apuntar a esa variable, apuntará al lugar en la pila donde estaba esa variable. Podría ser un valor utilizado por otra función o un valor sin sentido.
str nunca será puntero colgando. Because it points to static address
donde residen los literales de cadena. En su mayoría, será de readonly
y global
para el programa cuando se cargue. Incluso si intenta liberar o modificar, arrojará segmentation fault
en plataformas con protección de memoria .