Comment retourner une structure anonyme en C?
en essayant un code, j'ai réalisé que le code suivant est compilé:
c prettyprint-override">struct { int x, y; } foo(void) {
}
il semble que nous définissions une fonction nommée foo
qui renvoie un anonyme struct
.
maintenant, ma question Est: est-ce qu'il arrive seulement de compiler avec mon compilateur ou est-ce légal C(99)? Si oui, quelle est la syntaxe correcte pour une instruction return et comment puis-je attribuer correctement la valeur retournée à une variable?
6 réponses
la structure que vous retournez n'est pas une structure anonyme. C norme définit un anonyme struct comme un membre d'une autre structure qui n'utilise pas la balise. Ce que vous retournez est une structure sans étiquette, mais comme ce n'est pas un membre, ce n'est pas anonyme. Gcc utilise le nom < anonyme > pour indiquer une structure sans étiquette.
disons que vous essayez de déclarer une structure identique dans la fonction.
struct { int x, y; } foo( void )
{
return ( struct { int x, y; } ){ 0 } ;
}
gcc s'en plaint: incompatible types lors d'un retour de type 'struct < anonyme>' mais 'struct < anonyme>' a été prévu
Apparemment, les types ne sont pas compatibles. En regardant dans la Norme, nous voyons que:
1: deux types ont un type compatible si leurs types sont les mêmes. Des règles supplémentaires permettant de déterminer si deux types sont compatibles sont décrites au 6.7.2 Pour les spécificateurs de type, au 6.7.3 pour les qualificatifs de type et au 6.7.6. pour declarators. de plus, deux types de structure, d'union ou énumérés sont déclarés dans une traduction distincte les unités sont compatibles si leurs étiquettes et leurs éléments satisfont aux exigences suivantes: si l'un est déclaré avec une étiquette, l'autre doit être déclaré avec la même étiquette. si les deux sont complétés n'importe où dans leurs unités de traduction respectives, alors les exigences supplémentaires suivantes s'appliquent: il doit y avoir une correspondance entre leurs membres, tels que chaque paire de membres correspondants sont déclarées avec des types compatibles; si un membre de la paire est déclarée avec un alignement prescripteur, l'autre est déclarée avec un équivalent d'alignement spécificateur; et si un membre de la paire est déclarée avec un nom, l'autre est déclarée avec le même nom. Pour deux structures, les membres correspondants sont déclarés dans le même ordre. Pour deux structures ou unions, les champs de bits correspondants doivent avoir les mêmes largeurs. Pour deux énumérations, correspondant les membres ont les mêmes valeurs.
la deuxième partie en gras, explique que si les deux struct sont sans étiquette, comme dans cet exemple, ils doivent suivre les exigences supplémentaires énumérées après cette partie, ce qu'ils font. Mais si vous remarquez la première partie en gras ils doivent être dans des unités de traduction séparées, les structures dans l'exemple ne le sont pas. Ils ne sont donc pas compatibles et le code n'est pas valide.
il est impossible de faire le code correct car si vous déclarez une structure (struct) et de l'utiliser dans cette fonction, vous devez utiliser une balise, qui viole la règle que les deux ont des structures à avoir la même balise:
struct t { int x, y; } ;
struct { int x, y; } foo( void )
{
struct t var = { 0 } ;
return var ;
}
Nouveau gcc se plaint: types incompatibles lors du retour de type 'struct t' mais 'struct < anonyme >' a été prévu
cela fonctionne dans ma version de GCC, mais semble être un piratage total. Peut-être utile dans le code généré automatiquement où vous ne voulez pas faire face à la complexité supplémentaire de générer des étiquettes de structure uniques, mais je suis en quelque sorte d'étirement pour venir avec même cette rationalisation.
struct { int x,y; }
foo(void) {
typeof(foo()) ret;
ret.x = 1;
ret.y = 10;
return ret;
}
main()
{
typeof(foo()) A;
A = foo();
printf("%d %d\n", A.x, A.y);
}
aussi, cela dépend de la présence de typeof () dans le compilateur -- GCC et LLVM semblent le supporter, mais je suis sûr que beaucoup de compilateurs ne le font pas.
vous ne pouvez probablement pas explicitementreturn
une valeur agrégée de votre fonction (à moins que vous n'utilisiez un typeof
extension pour obtenir le type de résultat).
La morale de l'histoire est que même si vous pouvez déclarer une fonction retournant un anonymestruct
vous devez pratiquementne.
à la place, nommez le struct
et code:
struct twoints_st { int x; int y; };
struct twoints_st foo (void) {
return ((struct twoints_st) {2, 3});
};
Notez qu'il est syntaxiquement ok, mais généralement un comportement non défini lors de l'exécution, pour avoir une fonction sans return
(par exemple, vous pourriez l'appeler exit
à l'intérieur). Mais pourquoi voudriez-vous coder le (probablement légal) :
struct { int xx; int yy; } bizarrefoo(void) { exit(EXIT_FAILURE); }
cela fonctionne jusqu'à la dernière version de GCC. Il est particulièrement utile pour créer des tableaux dynamiques avec des macros. Par exemple:
#define ARRAY_DECL(name, type) struct { int count; type *array; } name
alors vous pouvez faire le tableau avec realloc, etc. Ceci est utile parce qu'alors vous pouvez créer un tableau dynamique avec N'importe quel type, et il y a une façon de les faire tous. Autrement vous finiriez par utiliser beaucoup de void *
' s et ensuite écrire des fonctions pour obtenir réellement les valeurs de retour avec des moulages et autres. Vous pouvez éviter tout cela avec des macros, c'est leur beauté.
Ou vous pouvez créer une récursion infinie:
struct { int x, y; } foo(void) {
return foo();
}
ce qui, je pense, est tout à fait légal.
Voici une façon de retourner des structures anonymes en C++14 sans aucun piratage que je viens de découvrir.
(C++ 11 devrait être suffisant, je suppose)
Dans mon cas, une fonction intersect()
retourne std::pair<bool, Point>
qui n'est pas très descriptif, donc j'ai décidé de faire un type personnalisé pour le résultat.
J'aurais pu faire un distinct struct
mais ça n'en valait pas la peine puisque je n'en aurais besoin que pour ce cas particulier; c'est pourquoi j'ai utilisé un anonyme struct.
auto intersect(...params...) {
struct
{
Point point;
bool intersects = false;
} result;
// do stuff...
return result;
}
Et maintenant, au lieu de le laid
if (intersection_result.first) {
Point p = intersection_result.second
je peux utiliser le beaucoup mieux à la recherche de:
if (intersection_result.intersects) {
Point p = intersection_result.point;