Otro “return-local-addr”: “la función devuelve la dirección de la variable local”

Lo sé, esta es una pregunta muy, muy común. He leído, por ejemplo, esto , eso , y esto también . Solía ​​pensar que devolver la dirección de una variable local es una idea muy mala. Solía ​​pensar que deberías mejor:

  • asignar memoria a través de, por ejemplo, malloc
  • hacer la variable local estática o
  • pasar el puntero como argumento

pero luego intenté esto:

#include  char * foo_1(); char ** foo_2(); int main() { char * p_1 = foo_1(); char ** p_2 = foo_2(); printf("\n [%s] \n", p_1); printf("\n [%s] \n", *p_2); return 0; } char * foo_1() { char * p = "bar"; return p; } char ** foo_2() { char * p = "bar"; return &p; } 

Compilo con -pedantic -pedantic-errors y obtengo la advertencia esperada: la función devuelve la dirección de la variable local [-Wreturn-local-addr]

¡Pero solo para foo_2 ()! foo_1 () funciona bien. ¿Alguien sabe por qué y si este comportamiento indefinido?

 char * foo_1() { char * p = "bar"; return p; } 

Aquí no está devolviendo la dirección de un objeto local, sino el valor del puntero de un puntero que apunta a una cadena literal. Los literales de cadena tienen una duración de almacenamiento estático y está bien devolver un puntero a un literal de cadena. Cuando foo_1 regresa, el objeto p se destruye (duración del almacenamiento automático) pero no la "bar" (duración del almacenamiento estático).

En foo_2() está devolviendo la dirección de la variable local p por lo que recibe la advertencia.

En foo_1() está devolviendo la dirección de la cadena literal, lo cual está bien ya que tiene una duración de almacenamiento estático.

En foo_2() , está devolviendo la dirección de una variable local (no static ). Esa dirección se vuelve indeterminada cuando la función regresa, porque el objeto apuntado ya no existe, por lo tanto, la advertencia.

En foo_1() , estás devolviendo el valor de una variable local. No hay problema en absoluto haciendo eso; no es peor que

 int foo_3(void) { int local = 42; return local; } 

que devuelve el valor de local .

En foo_1() , dado que la variable cuyo valor está devolviendo es un puntero, aún podría invocar un comportamiento indefinido si ese valor fuera cuestionable. Por ejemplo:

 int foo_1a(void) { char arr[] = "bar"; char *p = arr; // or equivalently, &arr[0] return p; } 

Aquí todavía está devolviendo el valor de una variable local (que está bien), pero ese valor es la dirección de otra variable local, por lo que el valor del puntero devuelto se vuelve inválido tan pronto como la función regresa.

Es menos probable que un comstackdor advierta sobre foo_1a que sobre foo_2 , porque es menos probable que pueda determinar que el valor de p cuando se ejecuta la instrucción return es problemático. De hecho, el lenguaje no requiere un diagnóstico para este tipo de cosas. Los comstackdores pueden hacer un buen trabajo de detección y advertencia sobre algunas instancias de comportamiento indefinido, pero no de todas.

Línea inferior: su función foo_1() se comporta bien. El valor del puntero que devuelve es la dirección de una cadena literal, que tiene una duración de almacenamiento estática (es decir, existe durante toda la vida útil del progtwig).

Sin embargo, dado que la modificación de la matriz estática tiene un comportamiento indefinido, sería conveniente devolver la dirección como un const char* lugar de como un char* , por lo que es menos probable que la persona que llama intente modificar la cadena literal. La const también sirve como documentación para cualquier lector humano de que el valor apuntado no debe modificarse.

 char * p_1 = foo_1(); 

esta bien La desreferenciación p_1 no es un problema siempre que no modifiques nada a lo que p_1 apunta, ya que foo_1 devuelve un puntero a un literal de cadena que se almacena en la sección de solo lectura del progtwig.

 char ** p_2 = foo_2(); 

No está bien. La desreferenciación de p_2 es causa de comportamiento indefinido ya que p_2 apunta a un objeto que se ha eliminado.