una retornar puede llamar funciones funcion fuente estructuras estructura ejercicios ejemplos devolver con codigo anidadas c++ c function struct return-type

retornar - ¿Es seguro devolver una estructura en C o C++?



puede una funcion devolver un struct (11)

Agreguemos una segunda parte a la pregunta: ¿esto varía según el compilador?

De hecho lo hace, como descubrí para mi dolor: http://sourceforge.net/p/mingw-w64/mailman/message/33176880/

Estaba usando gcc en win32 (MinGW) para llamar a las interfaces COM que devolvieron las estructuras. Resulta que MS lo hace de manera diferente a GNU y mi programa (gcc) se estrelló con una pila rota.

Podría ser que MS tenga un terreno más alto aquí, pero lo único que me importa es la compatibilidad ABI entre MS y GNU para construir en Windows.

Si es así, ¿cuál es el comportamiento de las últimas versiones de compiladores para equipos de escritorio: gcc, g ++ y Visual Studio?

Puede encontrar algunos mensajes en una lista de correo de Wine sobre cómo la MS parece hacerlo.

Lo que entiendo es que esto no debería hacerse, pero creo que he visto ejemplos que hacen algo como esto (el código de la nota no es necesariamente sintácticamente correcto, pero la idea está allí)

typedef struct{ int a,b; }mystruct;

Y luego aquí está una función

mystruct func(int c, int d){ mystruct retval; retval.a = c; retval.b = d; return retval; }

Entendí que siempre debemos devolver un puntero a una estructura malloc''ed si queremos hacer algo como esto, pero estoy seguro de que he visto ejemplos que hacen algo como esto. ¿Es esto correcto? Personalmente, siempre devuelvo un puntero a una estructura malloc''ed o simplemente paso por referencia a la función y modifico los valores allí. (Debido a que entiendo que una vez que el alcance de la función ha terminado, se puede sobrescribir la pila que se utilizó para asignar la estructura).

Agreguemos una segunda parte a la pregunta: ¿esto varía según el compilador? Si es así, ¿cuál es el comportamiento de las últimas versiones de compiladores para equipos de escritorio: gcc, g ++ y Visual Studio?

¿Pensamientos sobre el asunto?


Es perfectamente legal, pero con estructuras grandes hay dos factores que deben tenerse en cuenta: la velocidad y el tamaño de la pila.


Es perfectamente seguro devolver una estructura como lo ha hecho.

Sin embargo, basado en esta afirmación: porque mi entendimiento es que una vez que el alcance de la función termina, cualquier pila que se usó para asignar la estructura puede sobrescribirse, me imagino solo un escenario donde cualquiera de los miembros de la estructura se asignó dinámicamente ( malloc''ed o new''ed), en cuyo caso, sin RVO, los miembros dinámicamente asignados se destruirán y la copia devuelta tendrá un miembro que señale a la basura.


Es perfectamente seguro, y no está mal hacerlo. Además: no varía según el compilador.

Generalmente, cuando (como su ejemplo) su estructura no es demasiado grande, yo diría que este enfoque es incluso mejor que devolver una estructura malloc (el malloc es una operación costosa).


Es perfectamente seguro.

Estás regresando por valor. Lo que llevaría a un comportamiento indefinido es si regresas por referencia.

//safe mystruct func(int c, int d){ mystruct retval; retval.a = c; retval.b = d; return retval; } //undefined behavior mystruct& func(int c, int d){ mystruct retval; retval.a = c; retval.b = d; return retval; }

El comportamiento de su fragmento es perfectamente válido y definido. No varía según el compilador. ¡Está bien!

Personalmente, siempre devuelvo un puntero a una estructura malloc''ed

No deberias. Debe evitar la memoria asignada dinámicamente cuando sea posible.

o simplemente haga un pase por referencia a la función y modifique los valores allí.

Esta opción es perfectamente válida. Es una cuestión de elección. En general, puede hacer esto si desea devolver algo más desde la función, mientras modifica la estructura original.

Porque mi entendimiento es que una vez que el alcance de la función termina, cualquier pila que se usó para asignar la estructura se puede sobrescribir

Esto está mal. Quiero decir, es un poco correcto, pero devuelves una copia de la estructura que creaste dentro de la función. Teóricamente En la práctica, RVO puede y probablemente ocurrirá. Lea sobre la optimización del valor de retorno. Esto significa que aunque retval parece estar fuera del alcance cuando la función finaliza, en realidad podría construirse en el contexto de llamada, para evitar la copia adicional. Esta es una optimización que el compilador es libre de implementar.


La seguridad depende de cómo se implementó la estructura en sí. Acabo de tropezar con esta pregunta al implementar algo similar, y aquí está el problema potencial.

El compilador, al devolver el valor realiza algunas operaciones (entre otras posibles):

  1. Llama al constructor de copias mystruct(const mystruct&) ( this es una variable temporal fuera de la función func asignada por el compilador)
  2. llama al destructor ~mystruct sobre la variable que se asignó dentro de func
  3. llama a mystruct::operator= si el valor devuelto está asignado a otra cosa con =
  4. llama al destructor ~mystruct sobre la variable temporal utilizada por el compilador

Ahora, si mystruct es tan simple como el descrito aquí, todo está bien, pero si tiene un puntero (como char* ) o una administración de memoria más complicada, todo depende de cómo mystruct::operator= , mystruct(const mystruct&) , y ~mystruct están implementados. Por lo tanto, sugiero precauciones al devolver estructuras de datos complejas como valor.


La vida útil del objeto mystruct en tu función termina cuando sales de la función. Sin embargo, está pasando el objeto por valor en la declaración de devolución. Esto significa que el objeto se copia de la función a la función de llamada. El objeto original se ha ido, pero la copia vive.


No es seguro devolver una estructura. Me encanta hacerlo yo mismo, pero si alguien agregará un constructor de copia a la estructura devuelta más tarde, se llamará al constructor de copia. Esto podría ser inesperado y puede romper el código. Este error es muy difícil de encontrar.

Tenía una respuesta más elaborada, pero al moderador no le gustó. Entonces, a su juicio, mi consejo es corto.


No solo es seguro devolver una struct en C (o una class en C ++, donde struct -s son realmente class -es con public: predeterminado public: miembros), pero una gran cantidad de software lo está haciendo.

Por supuesto, al devolver una class en C ++, el lenguaje especifica que se llamará a algún destructor o constructor en movimiento, pero hay muchos casos en que el compilador podría optimizarlo.

Además, el Linux x86-64 ABI especifica que devolver una struct con dos valores escalares (por ejemplo, punteros o long ) se realiza a través de registros ( %rax y %rdx ), por lo que es muy rápido y eficiente. Entonces, para ese caso particular, probablemente sea más rápido devolver una struct campos de dos escalares que hacer cualquier otra cosa (por ejemplo, almacenarlas en un puntero pasado como argumento).

Devolver una struct campo de dos struct es mucho más rápido que malloc -ing y devolver un puntero.


También estaré de acuerdo con sftrabbit, Life de hecho termina y el área de la pila se aclara, pero el compilador es lo suficientemente inteligente como para garantizar que todos los datos se recuperen en los registros o de alguna otra manera.

Un ejemplo simple para la confirmación se da a continuación. (Tomado del ensamblado del compilador Mingw)

_func: push ebp mov ebp, esp sub esp, 16 mov eax, DWORD PTR [ebp+8] mov DWORD PTR [ebp-8], eax mov eax, DWORD PTR [ebp+12] mov DWORD PTR [ebp-4], eax mov eax, DWORD PTR [ebp-8] mov edx, DWORD PTR [ebp-4] leave ret

Puede ver que el valor de b se ha transmitido a través de edx. mientras que el eax predeterminado contiene valor para a.


Un tipo de estructura puede ser el tipo para el valor devuelto por una función. Es seguro porque el compilador creará una copia de struct y devolverá la copia, no la estructura local de la función.

typedef struct{ int a,b; }mystruct; mystruct func(int c, int d){ mystruct retval; cout << "func:" <<&retval<< endl; retval.a = c; retval.b = d; return retval; } int main() { cout << "main:" <<&(func(1,2))<< endl; system("pause"); }