¿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és de que char* foo(int)
termina, 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?
c
function
local-variables
string-literals
lifetime
usuario113454
fuente
fuente
int rc
. Su vida finaliza en cada una de lasreturn
-s. Los punteros que está devolviendo son a cadenas literales. Los literales de cadena tienen una duración de almacenamiento estática: su vida útil es al menos tan larga como la del programa.strerror
Evidentemente, nunca ha visto la función.Respuestas:
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 que finaliza 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 por la implementación. Los literales de cadena son diferentes de las variables locales y permanecen vivos durante toda la vida útil del programa. Tienen una 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 un literal de cadena es un comportamiento indefinido (UB). Los programas de usuario no pueden modificar el contenido de una cadena literal.
Por lo tanto, siempre se recomienda usar un
const
while declarando una cadena literal.const char*p = "string";
en vez de,
char*p = "string";
De hecho, en C ++ está en desuso declarar un literal de cadena sin el
const
aunque no en C. Sin embargo, declarar un literal de cadena con unconst
le da la ventaja de que los compiladores normalmente le darían una advertencia en caso de que intente modificar el literal de cadena en 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 Uundefined Behavior strcpy(str2,source); // Compiler issues a warning return 0; }
Salida:
Observe que el compilador advierte para el segundo caso, pero no para el primero.
Para responder a la pregunta de un par de usuarios aquí:
¿Cuál es el trato 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 literales (
int
,float
,char
, etc.) son los valores de r (el estándar C utiliza el término el valor de una expresión para estos) y su dirección no puede ser tomada en absoluto.[Ref 1] Estándar C99 6.4.5 / 5 "Literales de cadena - Semántica":
fuente
char (*)[4]
. Esto se debe a que el tipo de "abc" eschar[4]
y el puntero a una matriz de 4 caracteres se declara comochar (*)[4]
, Entonces, si necesita tomar la dirección, debe hacerlo comochar (*a)[4] = &"abc";
y Sí, es válido.char[4]
. (Debido a'\0'
)char const s[] = "text";
no no hacers
un literal de caracteres, y por lo tantos
va a ser destruida al final del alcance, por lo que cualquier punteros que sobreviven a que se cuelgan.Es valido. 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:
Y para C ++ en la sección 2.14.5, párrafos 8-11:
fuente
Los literales de cadena son válidos para todo el programa (y no se asignan ni a la pila), por lo que serán válidos.
Además, los literales de cadena son de solo lectura, por lo que (para un buen estilo) tal vez debería cambiar
foo
aconst char *foo(int)
fuente
&"abc"
no lo eschar*
. es una dirección de matriz y su tipo eschar(*)[4]
. Sin embargo, ambosreturn &"abc";
ychar *a="abc";return a;
son válidos.const
, y será completamente legal, pero seguirá siendo de mal estilo.Sí, es un código válido, consulte el caso 1 a continuación. Puede devolver cadenas C de una función de forma segura al menos de estas formas:
const char*
a una cadena literal. No se puede modificar y 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. Podría tener sentido si realmente necesita pasar un puntero de función en algún lugar, por lo que necesita una función que devuelva una cadena.char*
oaconst char*
un búfer de carbón estático. No debe ser liberado por la persona que llama. Puede ser modificado (ya sea por la persona que llama si no es constante, o por la función que lo devuelve), pero una función que devuelve esto no puede (fácilmente) tener múltiples búferes, por lo que no es (fácilmente) seguro para subprocesos, y la persona que llama puede necesitar para copiar el valor devuelto antes de volver a llamar a la función.char*
a un búfer asignado conmalloc
. Se puede modificar, pero normalmente la persona que llama debe liberarlo explícitamente y tiene la sobrecarga de asignación de montón.strdup
es de este tipo.const char*
oachar*
un búfer, que se pasó como argumento a la función (el puntero devuelto no necesita apuntar al primer elemento del búfer de argumentos). Deja la responsabilidad de la gestión del búfer / memoria a quien llama. Muchas funciones de cadena estándar son de este tipo.Un problema es que mezclarlos en una función puede resultar complicado. La persona que llama necesita saber cómo debe manejar el puntero devuelto, cuánto tiempo es válido y si la persona que llama debe liberarlo, y no hay una forma (agradable) de determinar eso en tiempo de ejecución. Por lo tanto, no puede, por ejemplo, tener una función, que a veces devuelve un puntero a un búfer asignado al montón que la persona que llama necesita
free
, y a veces un puntero a un valor predeterminado de la cadena literal, que la persona que llama no debefree
.fuente
Buena pregunta. En general, tendría razón, pero su ejemplo es la excepción. El compilador asigna estáticamente memoria global para un literal de cadena. Por lo tanto, la dirección devuelta por su función es válida.
Que esto sea así es una característica bastante conveniente de C, ¿no? Permite que una función devuelva un mensaje precompuesto sin obligar al programador a preocuparse por la memoria en la que se almacena el mensaje.
Véase también la observación correcta de @ asaelr re
const
.fuente
const char *a = "abc";
, omitiendo el&
. La razón es que una cadena entre comillas dobles se resuelve en la dirección de su carácter inicial.Las variables locales solo son válidas dentro del alcance que están declaradas, sin embargo, no declara ninguna variable local 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 durante toda la ejecución del programa, tal como lo
static
haría una variable global o una.Si le preocupa que lo que está haciendo no sea válido indefinido, debería activar las advertencias del compilador para ver si hay algo que esté haciendo mal.
fuente
&"abc"
no es de tipochar*
, sin embargo ambos"abc"
y&"abc"
son válidos durante toda la ejecución del programa.str
nunca será un puntero colgante, porque apunta a una dirección estática donde residen los literales de cadena.Será principalmente de solo lectura y global para el programa cuando se cargue.
Incluso si intenta liberar o modificar, arrojará una falla de segmentación en plataformas con protección de memoria .
fuente
Se asigna una variable local en la pila. Una vez finalizada la función, la variable queda fuera de alcance y ya no es accesible en el código. Sin embargo, si tiene un puntero global (o simplemente, que aún no está fuera de 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.
fuente
En el ejemplo anterior mostrado por usted, en realidad está devolviendo los punteros asignados a cualquier función que llame a lo anterior. Entonces no se convertiría en un puntero local. Además, para los punteros que se necesitan devolver, la memoria se asigna en el segmento global.
fuente