web-dev-qa-db-fra.com

Comment retourner une structure anonyme en C?

En essayant du code, j'ai réalisé que le code suivant compilait:

struct { int x, y; } foo(void) {
}

Il semble que nous définissions une fonction nomméefooqui retourne un anonymestruct.

Maintenant, ma question est: est-ce que ça arrive seulement de compiler avec mon compilateur ou est-ce légal C (99)? Si tel est le cas, quelle est la syntaxe correcte pour une instruction return et comment puis-je affecter correctement la valeur renvoyée à une variable?

33
Askaga

La structure que vous retournez n'est pas une structure anonyme. Le standard C définit une structure anonyme en tant que membre d’une autre structure n’utilisant pas de balise. Ce que vous retournez est une structure sans tag, mais comme ce n'est pas un membre, ce n'est pas anonyme. Gcc utilise le nom <anonymous> pour indiquer une structure sans balise.

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: types incompatibles lors du renvoi du type 'struct <anonymous>' mais 'struct <anonymous>' était attendu

Apparemment, les types ne sont pas compatibles. En regardant dans la norme, nous voyons que:

6.2.7 Type compatible et type composite

1: Deux types sont compatibles si leurs types sont identiques. Des règles supplémentaires permettant de déterminer si deux types sont compatibles sont décrites dans 6.7.2 pour les spécificateurs de type, dans 6.7.3 pour les qualificateurs de type et dans 6.7.6 pour les déclarateurs. En outre, deux types de structure, d'union ou énumérés déclarés dans une traduction séparée sont compatibles si leurs balises et leurs membres satisfont aux exigences suivantes: Si l'une est déclarée avec une balise, l'autre doit être déclarée avec la même balise . Si les deux sont terminés n'importe où dans leurs unités de traduction respectives, les conditions supplémentaires suivantes s'appliquent: il doit exister une correspondance individuelle entre leurs membres de telle sorte que chaque paire de membres correspondants soit déclarée avec des types compatibles; si un membre de la paire est déclaré avec un spécificateur d'alignement, l'autre est déclaré avec un spécificateur d'alignement équivalent; et si un membre de la paire est déclaré avec un nom, l'autre est déclaré avec le même nom. Pour deux structures, les membres correspondants doivent être 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, les membres correspondants doivent avoir les mêmes valeurs.

La deuxième partie en gras explique que si les deux structures ne contiennent pas de balise, comme dans cet exemple, elles doivent respecter les exigences supplémentaires énumérées à la suite de cette partie, ce qu'elles font. Mais si vous remarquez que la première partie en gras doit être en unités de traduction distinctes, les structures de l'exemple ne le sont pas. Donc, ils ne sont pas compatibles et le code n'est pas valide. 

Il est impossible de rendre le code correct, car si vous déclarez une structure et que vous l'utilisez dans cette fonction, vous devez utiliser une balise, qui enfreint la règle selon laquelle les deux structures doivent avoir la même balise:

struct t { int x, y; } ;

struct { int x, y; } foo( void )   
{
    struct t var = { 0 } ;

return var ;
}

Encore une fois, gcc se plaint: types incompatibles lors du renvoi du type 'struct t' mais 'struct <anonymous>' était attendu

24
2501

Cela fonctionne dans ma version de GCC, mais semble être un piratage total. Peut-être utile dans le code généré automatiquement lorsque vous ne souhaitez pas gérer la complexité supplémentaire liée à la génération de balises de structure uniques, mais j'essaie en quelque sorte de parvenir à 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);
}

En outre, cela dépend de typeof () présent dans le compilateur - GCC et LLVM semblent le supporter, mais je suis sûr que beaucoup de compilateurs ne le font pas.

13
John Brennen

Vous ne pouvez probablement pas explicitement return une valeur globale de votre fonction (sauf si vous utilisez une extension typeof pour obtenir le type du résultat).

La morale de l'histoire est que même si vous pouvez déclarer une fonction retournant un anonyme struct, vous devriez pratiquement ne faites jamais cela .

Au lieu de cela, nommez la struct et le code:

struct twoints_st { int x; int y; };
struct twoints_st foo (void) {
   return ((struct twoints_st) {2, 3});
};

Notez qu’il est correct du point de vue de la syntaxe, mais généralement du comportement lors de l’exécution, d’avoir une fonction sans return (par exemple, vous pouvez appeler exit à l’intérieur). Mais pourquoi voudriez-vous coder le (probablement légal):

struct { int xx; int yy; } bizarrefoo(void) { exit(EXIT_FAILURE); }
9

Cela fonctionne jusqu'à la dernière version de GCC. C'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

Vous pouvez ensuite créer le tableau avec realloc, etc. Ceci est utile car vous pouvez créer un tableau dynamique avec TOUT type et il existe un moyen de les créer tous. Sinon, vous utiliseriez beaucoup de void *, puis écrivez des fonctions pour récupérer les valeurs avec des transtypages, etc. Vous pouvez raccourcir tout cela avec des macros; c'est leur beauté.

1
Gophyr

Voici un moyen de renvoyer des structures anonymes en C++ 14 sans aucun piratage que je viens de découvrir.
(C++ 11 devrait suffire, je suppose)
Dans mon cas, une fonction intersect() renvoie std::pair<bool, Point>, ce qui n’est pas très descriptif. J’ai donc décidé de créer un type personnalisé pour le résultat.
J'aurais pu créer une struct distincte, mais cela ne valait pas la peine, car je n'en aurais besoin que pour ce cas particulier; c'est pourquoi j'ai utilisé une structure anonyme.

auto intersect(...params...) {
    struct
    {
        Point point;
        bool intersects = false;
    } result;

    // do stuff...

    return result;
}

Et maintenant, au lieu du moche

if (intersection_result.first) {
    Point p = intersection_result.second

Je peux utiliser le beaucoup plus beau:

if (intersection_result.intersects) {
    Point p = intersection_result.point;
1
Al.G.

Ou vous pouvez créer une récursion infinie:

struct { int x, y; } foo(void) {
   return foo();
}

Ce que je pense est complètement légal.

0
AnArrayOfFunctions