tipos - llamar una estructura en una funcion
Devuelve una `struct` de una funciĆ³n en C (8)
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 compilador. Considera lo siguiente:
struct MyObj{
double x, y;
};
struct MyObj foo(){
struct MyObj a;
a.x = 10;
a.y = 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();
compila bien? ¿Significa algo diferente de struct b = a;
? Tal vez la pregunta que debe hacerse es: ¿qué hace exactamente la declaración de return
junto con =
sign?
[edit]: Ok, solo estaba señalando que struct b = a
es un error de sintaxis - ¡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?
Al hacer una llamada como a = foo();
, el compilador puede insertar la dirección de la estructura de resultados en la pila 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 compilador 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 sintaxis. 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)
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
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.
Puede asignar estructuras en C. a = b;
es sintaxis valida
Simplemente dejó una parte del tipo, la etiqueta struct, en su línea que no funciona.
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 programa 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 <stdio.h>
struct a {
int i;
};
struct a f(struct a x)
{
struct a r = x;
return r;
}
int main(void)
{
struct a x = { 12 };
struct a y = f(x);
printf("%d/n", y.i);
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 programas tienen el mismo comportamiento con respecto al valor de paso para el paso de parámetros, la asignación, etc.
#include <stdio.h>
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;
}
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<stdio.h>
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;
}
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 compiladores. Al igual que id, el contenido del nombre se copia a la variable de estructura asignada.