Devuelve una `struct` de una función en C

Hoy estaba enseñando a un par de amigos a usar C struct s. Uno de ellos me preguntó si podía devolver una struct de una función, a lo que respondí: “No, en lugar de eso, devolvería los punteros a las struct malloc dinámicamente”.

Viniendo de alguien que principalmente hace C ++, esperaba no poder devolver struct s por valores. En C ++ puede sobrecargar el operator = para sus objetos y tiene sentido completo tener una función para devolver su objeto por valor. En C, sin embargo, no tienes esa opción y eso me hizo pensar en lo que realmente está haciendo el comstackdor. Considera lo siguiente:

 struct MyObj{ double x, y; }; struct MyObj foo(){ struct MyObj a; ax = 10; ay = 10; return a; } int main () { struct MyObj a; a = foo(); // This DOES work struct b = a; // This does not work return 0; } 

Entiendo por qué struct b = a; no debería funcionar; no puede sobrecargar operator = para su tipo de datos. ¿Cómo es que a = foo(); comstack bien? ¿Significa algo diferente de struct b = a; ? Tal vez la pregunta que debe hacerse es: ¿qué hace exactamente la statement de return junto con = sign?

[edit]: Ok, solo estaba señalando que struct b = a es un error de syntax – ¡eso es correcto y yo soy un idiota! ¡Pero eso lo hace aún más complicado! ¡Usar struct MyObj b = a sí funciona! ¿Que me estoy perdiendo aqui?

Puede devolver una estructura desde una función (o usar el operador = ) sin ningún problema. Es una parte bien definida del lenguaje. El único problema con struct b = a es que no proporcionó un tipo completo. struct MyObj b = a funcionará bien. También puede pasar estructuras a funciones: una estructura es exactamente la misma que cualquier tipo integrado para el paso de parámetros, los valores de retorno y la asignación.

Aquí hay un progtwig de demostración simple que hace las tres cosas: pasa una estructura como parámetro, devuelve una estructura de una función y usa estructuras en las declaraciones de asignación:

 #include  struct a { int i; }; struct af(struct ax) { struct ar = x; return r; } int main(void) { struct ax = { 12 }; struct ay = f(x); printf("%d\n", yi); return 0; } 

El siguiente ejemplo es más o menos el mismo, pero usa el tipo de int integrado para fines de demostración. Los dos progtwigs tienen el mismo comportamiento con respecto al valor de paso para el paso de parámetros, la asignación, etc.

 #include  int f(int x) { int r = x; return r; } int main(void) { int x = 12; int y = f(x); printf("%d\n", y); return 0; } 

Al hacer una llamada como a = foo(); , el comstackdor puede insertar la dirección de la estructura de resultados en la stack y pasarla como un puntero “oculto” a la función foo() . Efectivamente, podría convertirse en algo así como:

 void foo(MyObj *r) { struct MyObj a; // ... *r = a; } foo(&a); 

Sin embargo, la implementación exacta de esto depende del comstackdor y / o plataforma. Como señala Carl Norum, si la estructura es lo suficientemente pequeña, incluso podría transmitirse por completo en un registro.

La línea struct b no funciona porque es un error de syntax. Si lo expandes para incluir el tipo, funcionará bien

 struct MyObj b = a; // Runs fine 

Lo que C está haciendo aquí es esencialmente una memcpy desde la estructura fuente hasta el destino. Esto es cierto tanto para la asignación como para el retorno de los valores de la struct (y realmente de todos los demás valores en C)

sí, es posible que podamos pasar estructura y estructura de retorno también. Tenías razón, pero en realidad no pasaste el tipo de datos que debería ser como esta struct MyObj b = a.

En realidad, también llegué a saber cuándo estaba tratando de encontrar una mejor solución para devolver más de un valor para la función sin usar el puntero o la variable global.

A continuación, se muestra el ejemplo de lo mismo, que calcula la desviación de un alumno con respecto a la media.

 #include struct marks{ int maths; int physics; int chem; }; struct marks deviation(struct marks student1 , struct marks student2 ); int main(){ struct marks student; student.maths= 87; student.chem = 67; student.physics=96; struct marks avg; avg.maths= 55; avg.chem = 45; avg.physics=34; //struct marks dev; struct marks dev= deviation(student, avg ); printf("%d %d %d" ,dev.maths,dev.chem,dev.physics); return 0; } struct marks deviation(struct marks student , struct marks student2 ){ struct marks dev; dev.maths = student.maths-student2.maths; dev.chem = student.chem-student2.chem; dev.physics = student.physics-student2.physics; return dev; } 

Puede asignar estructuras en C. a = b; es syntax valida

Simplemente dejó una parte del tipo, la etiqueta struct, en su línea que no funciona.

Por lo que puedo recordar, las primeras versiones de C solo permitían devolver un valor que podía caber en un registro de procesador, lo que significa que solo se podía devolver un puntero a una estructura. La misma restricción aplicada a los argumentos de la función.

Las versiones más recientes permiten pasar objetos de datos más grandes como estructuras. Creo que esta característica ya era común durante los años ochenta o principios de los noventa.

Las matrices, sin embargo, aún se pueden pasar y devolver solo como punteros.

No hay problema para devolver una estructura. Será pasado por valor

Pero, ¿y si la estructura contiene algún miembro que tenga una dirección de una variable local?

 struct emp { int id; char *name; }; struct emp get() { char *name = "John"; struct emp e1 = {100, name}; return (e1); } int main() { struct emp e2 = get(); printf("%s\n", e2.name); } 

Ahora, aquí e1.name contiene una dirección de memoria local para la función get (). Una vez que get () regrese, la dirección local para el nombre se habrá liberado. ASÍ, en la persona que llama si intentamos acceder a esa dirección, puede causar una falla de segmentación, ya que estamos intentando liberar una dirección. Eso es malo..

Donde el e1.id será perfectamente válido ya que su valor será copiado a e2.id

Por lo tanto, siempre debemos tratar de evitar devolver direcciones de memoria local de una función.

Cualquier cosa malloced puede devolverse cuando lo desee

 struct emp { int id; char *name; }; struct emp get() { char *name = "John"; struct emp e1 = {100, name}; return (e1); } int main() { struct emp e2 = get(); printf("%s\n", e2.name); } 

funciona bien con versiones más nuevas de comstackdores. Al igual que id, el contenido del nombre se copia a la variable de estructura asignada.