Aujourd'hui , j'enseignais un couple d'amis comment utiliser C struct
s. L' un d'eux a demandé si vous pouviez revenir un struct
d'une fonction, à laquelle je répondis: « Non , vous avait renvoyer des pointeurs dynamiquement malloc
ed struct
s à la place. »
Venant de quelqu'un qui fait principalement du C ++, je m'attendais à ne pas pouvoir renvoyer struct
s par valeurs. En C ++, vous pouvez surcharger le operator =
pour vos objets et il est tout à fait logique d'avoir une fonction pour renvoyer votre objet par valeur. En C, cependant, vous n'avez pas cette option et cela m'a donc fait réfléchir à ce que fait réellement le compilateur. Considérer 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 ça a = foo();
compile bien? Cela signifie-t-il autre chose que struct b = a;
? Peut-être que la question à se poser est la suivante: que fait exactement l' return
énoncé associé au =
signe?
[modifier]: Ok, on m'a juste signalé struct b = a
une erreur de syntaxe - c'est correct et je suis un idiot! Mais cela rend les choses encore plus compliquées! L'utilisation struct MyObj b = a
fonctionne en effet! Qu'est-ce que j'oublie ici?
struct b = a;
est une erreur de syntaxe. Et si vous essayezstruct MyObj b = a;
?struct MyObj b = a;
semble fonctionner :)Réponses:
Vous pouvez renvoyer une structure à partir d'une fonction (ou utiliser l'
=
opérateur) sans aucun problème. C'est une partie bien définie de la langue. Le seul problèmestruct b = a
est que vous n'avez pas fourni de type complet.struct MyObj b = a
fonctionnera très bien. Vous pouvez également transmettre des structures à des fonctions - une structure est exactement la même que tout type intégré à des fins de passage de paramètres, de valeurs de retour et d'affectation.Voici un programme de démonstration simple qui fait les trois: passe une structure en tant que paramètre, retourne une structure à partir d'une fonction et utilise des structures dans les instructions d'assignation:
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 le passage par valeur pour le passage de paramètres, l'affectation, etc.:la source
Lors d'un appel tel que
a = foo();
, le compilateur peut pousser l' adresse de la structure de résultat sur la pile et la passe comme un pointeur "caché" vers lafoo()
fonction. En effet, cela pourrait devenir quelque chose comme:Cependant, l'implémentation exacte de ceci dépend du compilateur et / ou de la plate-forme. Comme le note Carl Norum, si la structure est suffisamment petite, elle peut même être renvoyée complètement dans un registre.
la source
foo
frame de pile. Il doit être dans un endroit qui survit après le retour defoo
.foo
. À l'intérieur de la fonctionfoo
, vous venez de faire le devoir*r = a
à la fin ferait (effectivement) une copie de la variable locale dans la variable de l'appelant. Je dis "effectivement" parce que le compilateur pourrait implémenter RVO et éliminera
complètement la variable locale .c return struct
: ils savent que dans cdecleax
est retourné par valeur et que les structures en général ne rentrent pas à l'intérieureax
. C'est ce que je cherchais.La
struct b
ligne ne fonctionne pas car c'est une erreur de syntaxe. Si vous le développez pour inclure le type, cela fonctionnera très bienCe que C fait ici est essentiellement un
memcpy
de la structure source à la destination. Ceci est vrai à la fois pour l'affectation et le retour destruct
valeurs (et vraiment pour toutes les autres valeurs en C)la source
memcpy
dans ce cas - du moins, si la structure est raisonnablement grande.memcpy
fonction pour les situations de structure. Vous pouvez créer un programme de test rapide et voir GCC le faire, par exemple. Pour les types intégrés qui ne se produiront pas, ils ne sont pas assez grands pour déclencher ce type d'optimisation.memcpy
lequel je travaille n'a pas de symbole défini, donc nous rencontrons souvent des erreurs d'éditeur de liens "symbole indéfini" lorsque le compilateur décide d'en recracher un seul.oui, il est possible que nous puissions également passer la structure et la structure de retour. Vous aviez raison mais vous n'avez en fait pas passé le type de données qui devrait ressembler à cette structure MyObj b = a.
En fait, j'ai également appris à savoir quand j'essayais de trouver une meilleure solution pour renvoyer plus d'une valeur de fonction sans utiliser de pointeur ou de variable globale.
Voici maintenant l'exemple du même, qui calcule l'écart des notes d'un élève par rapport à la moyenne.
la source
Autant que je me souvienne, les premières versions de C permettaient uniquement de renvoyer une valeur qui pouvait entrer dans un registre de processeur, ce qui signifie que vous ne pouviez renvoyer qu'un pointeur vers une structure. La même restriction s'appliquait aux arguments de fonction.
Des versions plus récentes permettent de faire circuler des objets de données plus volumineux comme des structures. Je pense que cette caractéristique était déjà courante dans les années 80 ou au début des années 90.
Les tableaux, cependant, peuvent toujours être passés et renvoyés uniquement sous forme de pointeurs.
la source
Vous pouvez affecter des structures en C.
a = b;
est une syntaxe valide.Vous avez simplement laissé une partie du type - la balise struct - dans votre ligne qui ne fonctionne pas.
la source
Il n'y a aucun problème à renvoyer une structure. Il sera passé par valeur
Mais que se passe-t-il si la structure contient un membre qui a l'adresse d'une variable locale
Maintenant, ici e1.name contient une adresse mémoire locale à la fonction get (). Une fois get () retourné, l'adresse locale du nom aurait été libérée. Donc, chez l'appelant, si nous essayons d'accéder à cette adresse, cela peut provoquer une erreur de segmentation, car nous essayons une adresse libérée. C'est mauvais..
Où comme e1.id sera parfaitement valide car sa valeur sera copiée dans e2.id
Donc, nous devons toujours essayer d'éviter de renvoyer les adresses de mémoire locale d'une fonction.
Tout ce qui est malléable peut être retourné au fur et à mesure
la source
fonctionne bien avec les nouvelles versions des compilateurs. Tout comme id, le contenu du nom est copié dans la variable de structure attribuée.
la source
struct var e2 adresse poussée comme arg dans la pile de l'appelé et les valeurs y sont assignées. En fait, get () renvoie l'adresse de e2 dans eax reg. Cela fonctionne comme un appel par référence.
la source