Renvoie une ' structure` d'une fonction en C

aujourd'Hui, j'enseigne à un couple d'amis comment utiliser C struct . L'un d'eux a demandé si vous pouviez retourner un struct à partir d'une fonction, à laquelle j'ai répondu: "Non! Vous retournerez dynamiquement les pointeurs à malloc ed struct s à la place."

venant de quelqu'un qui fait principalement C++, Je m'attendais à ne pas pouvoir retourner struct s par valeurs. En C++ , vous pouvez surcharger le operator = pour vos objets et fait tout le sens d'avoir un fonction pour retourner votre objet par valeur. En C, cependant, vous n'avez pas cette option et cela m'a fait penser à ce que le compilateur est en train de faire. Considérons ce qui suit:

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;
}    

je comprends pourquoi struct b = a; ne devrait pas fonctionner -- vous ne pouvez pas surcharger operator = pour votre type de données. Comment se fait-il que a = foo(); compile bien? Ça veut dire autre chose que struct b = a; ? Peut-être que la question à se poser est: qu'est-ce que la déclaration return en conjointement à = signe?

[edit]: Ok, je viens de pointer struct b = a est une erreur de syntaxe -- c'est correct et je suis un idiot! Mais cela rend les choses encore plus compliquées! Utiliser struct MyObj b = a fonctionne en effet! Ce qui me manque ici?

127
c
demandé sur Jonathan Leffler 2012-03-11 10:59:31

8 réponses

vous pouvez retourner une structure à partir d'une fonction (ou utiliser l'opérateur = ) sans aucun problème. C'est une partie bien définie du langage. Le seul problème avec struct b = a est que vous n'avez pas fourni un type complet. struct MyObj b = a marchera très bien. Vous pouvez passer les structures à fonctions aussi bien - une structure est exactement le même que n'importe quel type intégré pour les fins de passage de paramètre, valeurs de retour, et assignation.

Voici un programme de démonstration simple qui fait tous les trois-passe une structure comme paramètre, renvoie une structure d'une fonction, et utilise des structures dans les énoncés d'affectation:

#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;
}

l'exemple suivant est à peu près exactement le même, mais utilise le type intégré int à des fins de démonstration. Les deux programmes ont le même comportement en ce qui concerne la valeur de passage pour le passage de paramètres, l'affectation, 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;
}
158
répondu Carl Norum 2012-12-27 20:30:51

lors d'un appel tel que a = foo(); , le compilateur pourrait pousser l'adresse de la structure de résultat sur la pile et le passe comme un pointeur" caché "à la fonction foo() . Effectivement, cela pourrait devenir quelque chose comme:

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

foo(&a);

cependant, la mise en œuvre exacte de ceci dépend du compilateur et/ou de la plate-forme. Comme le fait remarquer Carl Norum, si la structure est assez petite, elle pourrait même être transmise complètement dans un inscrire.

26
répondu Greg Hewgill 2012-03-11 07:17:39

la ligne struct b ne fonctionne pas parce que c'est une erreur de syntaxe. Si vous développez pour inclure le type il fonctionne très bien

struct MyObj b = a;  // Runs fine

ce que C fait ici est essentiellement un memcpy de la structure source à la destination. Ceci est vrai pour l'assignation et le retour des valeurs struct (et vraiment toutes les autres valeurs en C)

12
répondu JaredPar 2012-03-11 07:03:37

oui, il est possible que nous puissions passer la structure et la structure de retour aussi bien. Vous aviez raison, mais en fait vous n'avez pas passé le type de données qui devrait être comme ceci struct MyObj b = a.

en fait, J'ai également appris quand j'essayais de trouver une meilleure solution pour retourner plus d'une valeurs pour la fonction sans utiliser le pointeur ou la variable globale.

maintenant ci-dessous est l'exemple pour le même, qui calculent le déviation des notes de l'élève à peu près moyenne.

#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;
}
6
répondu Aman 2016-11-25 16:36:18

Vous peut affecter les structures en C. a = b; est une syntaxe valide.

vous avez simplement oublié une partie du type -- la balise struct -- dans votre ligne qui ne fonctionne pas.

3
répondu DigitalRoss 2012-03-11 07:20:22

pour autant que je m'en souvienne, les premières versions de C ne permettaient de retourner qu'une valeur pourrait tenir dans un registre de processeur, ce qui signifie que vous ne pouvez retourner un pointeur à un struct. La même restriction s'appliquait aux arguments de fonction.

les versions plus récentes permettent de passer autour d'objets de données plus grands comme les structures. Je pense que cette caractéristique était déjà courante dans les années 80 ou au début des années 90.

tableaux, cependant, peut encore être passé et renvoyé uniquement à titre indicatif.

3
répondu Giorgio 2014-01-09 09:43:32

il n'y a aucun problème à renvoyer un struct. Il sera transmis en valeur

Mais, si la structure contient tout membre qui a une adresse d'une variable locale

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);
}

maintenant, ici e1.nom contient une adresse mémoire locale à la fonction get(). Une fois get() retourné, l'adresse locale du nom aurait été libérée. DONC, en l'appelant si nous essayons d'accéder à cette adresse, il peut provoquer une erreur de segmentation, comme nous essayons libéré adresse. Ce qui est mauvais..

où e1.id sera parfaitement valide car sa valeur sera copiée sur e2.id

donc, nous devrions toujours essayer d'éviter de retourner les adresses mémoire locales d'une fonction.

Tout ce que malloced peut être retourné comme et quand voulu

2
répondu Jagan 2017-03-28 19:33:19
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);
}

fonctionne très bien avec les nouvelles versions de compilateurs. Tout comme id, le contenu du nom est copié dans la variable de structure assignée.

0
répondu saroj panda 2017-05-14 19:02:02