web-dev-qa-db-de.com

Wie kann ich eine anonyme Struktur in C zurückgeben?

Beim Testen von Code wurde mir klar, dass der folgende Code kompiliert wird:

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

Anscheinend definieren wir eine Funktion mit dem Namenfoo, die eine anonymestructzurückgibt.

Nun ist meine Frage: Kommt es nur vor, dass es mit meinem Compiler kompiliert wird, oder ist das legal C (99)? Wenn ja, wie lautet die korrekte Syntax für eine return-Anweisung und wie kann ich den zurückgegebenen Wert einer Variablen korrekt zuweisen?

33
Askaga

Die von Ihnen zurückgegebene Struktur ist keine anonyme Struktur. C standard definiert eine anonyme Struktur als Mitglied einer anderen Struktur, die kein Tag verwendet. Was Sie zurückgeben, ist eine Struktur ohne Tag, aber da es kein Mitglied ist, ist es nicht anonym. Gcc verwendet den Namen <anonymous> , um eine Struktur ohne Tag anzugeben.

Nehmen wir an, Sie versuchen, eine identische Struktur in der Funktion zu deklarieren.

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

gcc beschwert sich darüber: Inkompatible Typen bei der Rückgabe des Typs 'struct <anonymous>', aber 'struct <anonymous>' wurde erwartet

Anscheinend sind die Typen nicht kompatibel. Wenn wir in den Standard schauen, sehen wir Folgendes:

6.2.7 Kompatibler Typ und Verbundtyp

1: Zwei Typen haben einen kompatiblen Typ, wenn ihre Typen gleich sind. Zusätzliche Regeln zur Feststellung, ob zwei Typen kompatibel sind, werden in Abschnitt 6.7.2 für Typenbezeichner, in 6.7.3 für Typqualifizierer und in 6.7.6 für Deklaratoren beschrieben. Außerdem sind zwei in einer separaten Übersetzung deklarierte Struktur-, Vereinigungs- und Aufzählungstypen units kompatibel, wenn ihre Tags und Member die folgenden Anforderungen erfüllen: Wenn eine mit einem Tag deklariert ist, wird der andere mit demselben Tag deklariert . Wenn beide innerhalb ihrer jeweiligen Übersetzungseinheiten abgeschlossen sind, gelten die folgenden zusätzlichen Anforderungen: Es muss eine Eins-zu-Eins-Korrespondenz zwischen ihren Mitgliedern bestehen, so dass jedes Paar von entsprechenden Mitgliedern mit kompatiblen Typen deklariert wird. Wenn ein Element des Paares mit einem Ausrichtungsspezifizierer deklariert wird, wird das andere mit einem äquivalenten Ausrichtungsspezifizierer deklariert. und wenn ein Mitglied des Paares mit einem Namen deklariert ist, wird das andere mit demselben Namen deklariert. Bei zwei Strukturen werden die entsprechenden Mitglieder in derselben Reihenfolge erklärt. Für zwei Strukturen oder Vereinigungen müssen entsprechende Bitfelder die gleiche Breite haben. Bei zwei Aufzählungen müssen die entsprechenden Mitglieder dieselben Werte haben.

Der zweite fette Teil erklärt, dass, wenn beide Strukturen ohne das Tag sind, wie in diesem Beispiel, sie zusätzliche Anforderungen erfüllen müssen, die nach diesem Teil aufgelistet sind, was sie tun. Wenn Sie jedoch den ersten fetten Teil bemerken, müssen sie sich in separaten Übersetzungseinheiten befinden, Strukturen im Beispiel nicht. Sie sind also nicht kompatibel und der Code ist nicht gültig. 

Es ist unmöglich, den Code richtig zu machen, da Sie, wenn Sie eine Struktur deklarieren und in dieser Funktion verwenden, ein Tag verwenden müssen, das gegen die Regel verstößt, dass beide Strukturen das gleiche Tag haben müssen:

struct t { int x, y; } ;

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

return var ;
}

Wieder beschwert sich gcc: Inkompatible Typen bei der Rückgabe des Typs 'struct t', aber 'struct <anonymous>' wurde erwartet

24
2501

Dies funktioniert in meiner GCC-Version, scheint aber ein totaler Hack. Vielleicht nützlich für automatisch generierten Code, bei dem Sie nicht mit der zusätzlichen Komplexität des Generierens eindeutiger Struktur-Tags umgehen möchten, aber ich bin irgendwie bemüht, selbst mit dieser Rationalisierung zu kommen.

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

Es hängt auch davon ab, ob typeof () im Compiler vorhanden ist - GCC und LLVM scheinen dies zu unterstützen, aber ich bin sicher, dass viele Compiler dies nicht tun.

13
John Brennen

Wahrscheinlich können Sie explizit return keinen Aggregatwert Ihrer Funktion (es sei denn, Sie verwenden eine typeof-Erweiterung, um den Typ des Ergebnisses zu erhalten).

Die Moral der Geschichte ist, dass selbst wenn Sie eine Funktion deklarieren können, die eine anonym struct zurückgibt, sollten Sie praktisch das niemals tun .

Benennen Sie stattdessen die struct und den Code:

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

Beachten Sie, dass es syntaktisch in Ordnung ist, aber im Allgemeinen undefiniertes Verhalten bei der Ausführung, eine Funktion ohne return zu haben (z. B. könnten Sie exit darin aufrufen). Aber warum solltest du das (wahrscheinlich legal) kodieren wollen:

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

Dies funktioniert bis zur neuesten Version von GCC. Dies ist besonders nützlich zum Erstellen dynamischer Arrays mit Makros. Zum Beispiel:

#define ARRAY_DECL(name, type) struct { int count; type *array; } name

Dann können Sie das Array mit realloc usw. erstellen. Dies ist nützlich, da Sie dann ein dynamisches Array mit ANY-Typ erstellen können und es eine Möglichkeit gibt, alle Arrays zu erstellen. Andernfalls würden Sie viele void *s verwenden und dann Funktionen schreiben, um die Werte tatsächlich mit Casts und so wieder herauszuholen. Sie können dies alles mit Makros verknüpfen. das ist ihre Schönheit.

1
Gophyr

Hier können Sie anonyme Strukturen in C++ 14 zurückgeben, ohne die gerade entdeckten Hacks.
(C++ 11 sollte ausreichen, denke ich)
In meinem Fall gibt eine Funktion intersect()std::pair<bool, Point> zurück, die nicht sehr beschreibend ist. Daher habe ich mich entschieden, einen benutzerdefinierten Typ für das Ergebnis zu erstellen.
Ich hätte eine eigene struct machen können, aber es hat sich nicht gelohnt, da ich sie nur für diesen speziellen Fall brauche; deshalb habe ich eine anonyme Struktur verwendet.

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

    // do stuff...

    return result;
}

Und jetzt anstelle des Hässlichen

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

Ich kann die viel besser aussehende verwenden:

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

Oder Sie können eine unendliche Rekursion erstellen:

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

Was ich für völlig legal halte.

0