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

Lesezeit: 8 Minuten

Benutzeravatar von Askaga
Askaga

Beim Ausprobieren von Code stellte ich fest, dass der folgende Code kompiliert wird:

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

Es scheint, als würden wir eine Funktion mit dem Namen definieren foo die eine anonyme zurückgibt struct.

Passiert es nur mit meinem Compiler zu kompilieren 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 korrekt einer Variablen zuweisen?

  • Scheint ein Hack von gcc zu sein. Mit C99 strict gibt es einen Fehler ideone.com/665vM2

    – Ankur

    9. Januar 2015 um 10:18 Uhr


  • @Shan: Die Funktionsdefinitionssyntax ist immer noch legal. Es beschwert sich lediglich über die fehlende Rückgabeerklärung (und meine Frage lautet, wie man in dieser Situation eine korrekte Rückgabeerklärung erstellt).

    – Askaga

    9. Januar 2015 um 10:21 Uhr

  • In C99 gibt es keine anonymen Strukturen. Du kannst das schaffen zusammengesetzte Literale aber sie haben immer einen lokalen Geltungsbereich.

    – Ludin

    9. Januar 2015 um 10:45 Uhr

  • Ich denke, die Formulierung in §6.7.2.1 ¶8 Das Vorhandensein einer Strukturdeklarationsliste in einem Struktur-oder-Vereinigungsspezifizierer deklariert einen neuen Typ innerhalb einer Übersetzungseinheit. schließt aus, dass eine Variable desselben Typs wie der definierte Rückgabewert erstellt werden kann, und schließt aus, dass ein zusammengesetztes Literal desselben Typs erstellt werden kann, und bedeutet daher, dass Sie tatsächlich keinen Wert des definierten Typs zurückgeben können. Abschnitt §6.2.7 behandelt die Kompatibilität zwischen Übersetzungseinheiten, was wichtig ist, aber §6.7.2.1 steuert, was innerhalb einer TU passiert. Der Code in der Frage ist “legal”, aber nutzlos, IMO.

    – Jonathan Leffler

    11. Juli 2016 um 4:28 Uhr

Benutzeravatar von 2501
2501

Die zurückgegebene Struktur ist keine anonyme Struktur. Der 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 um eine Struktur ohne Tag anzuzeigen.

Angenommen, 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 “, aber „struct “ wurde erwartet.

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

6.2.7 Kompatibler Typ und zusammengesetzter Typ

1: Zwei Typen haben einen kompatiblen Typ, wenn ihre Typen gleich sind. Zusätzliche Regeln zum Bestimmen, ob zwei Typen kompatibel sind, werden in 6.7.2 für Typbezeichner, in 6.7.3 für Typqualifizierer und in 6.7.6 für Deklaratoren beschrieben. Darüber hinaus werden zwei Struktur-, Vereinigungs- oder Aufzählungstypen in einer separaten Übersetzung deklariert Einheiten sind kompatibel, wenn ihre Tags und Mitglieder die folgenden Anforderungen erfüllen: Wenn eine mit einem Tag deklariert ist, muss die andere mit demselben Tag deklariert werden. Wenn beide irgendwo innerhalb ihrer jeweiligen Übersetzungseinheiten abgeschlossen werden, gelten die folgenden zusätzlichen Anforderungen: Es muss eine Eins-zu-Eins-Korrespondenz zwischen ihren Mitgliedern bestehen, so dass jedes Paar korrespondierender Mitglieder mit kompatiblen Typen deklariert wird; wenn ein Mitglied des Paars mit einem Ausrichtungsbezeichner deklariert wird, wird das andere mit einem äquivalenten Ausrichtungsbezeichner deklariert; und wenn ein Mitglied des Paars mit einem Namen deklariert wird, wird das andere mit demselben Namen deklariert. Für zwei Strukturen müssen entsprechende Elemente in derselben Reihenfolge deklariert werden. Für zwei Strukturen oder Vereinigungen sollen entsprechende Bitfelder die gleichen Breiten haben. Bei zwei Aufzählungen müssen entsprechende Mitglieder die gleichen Werte haben.

Der zweite fettgedruckte Teil erklärt, dass, wenn beide Strukturen ohne das Tag sind, wie in diesem Beispiel, sie den zusätzlichen Anforderungen folgen müssen, die nach diesem Teil aufgeführt sind, was sie auch tun. Aber wenn Sie den ersten fettgedruckten Teil bemerken, müssen sie in separaten Übersetzungseinheiten stehen, und Strukturen im Beispiel sind es nicht. Sie sind also nicht kompatibel und der Code ist ungültig.

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

struct t { int x, y; } ;

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

    return var ;
}

Wieder klagt GCC: inkompatible Typen bei Rückgabe des Typs „struct t“, aber „struct “ wurde erwartet

  • Hmm, interessant, ich habe diesen Teil des Standards noch nie gelesen. “Zwei Typen haben kompatible Typen, wenn ihre Typen gleich sind” scheint mir völliger Unsinn zu sein (und es könnte auf diese Situation zutreffen; wer weiß), aber das wollte ich nicht kommentieren. Darf ich daraus schließen, dass die beiden Typen in Ihrem Beispiel zwar nicht kompatibel sind, dies jedoch der Fall wäre beide mit einem ähnlichen Typ kompatibel sein, der in einer anderen Übersetzungseinheit definiert ist? Und dass also Kompatibilität keine transitive Relation ist?

    – Marc van Leeuwen

    9. Januar 2015 um 13:54 Uhr

  • @MarcvanLeeuwen Typen müssen mit vielen Ausnahmen gleich sein. Strukturen ohne Tags wären kompatibel, wenn sie in unterschiedlichen Übersetzungseinheiten definiert würden.

    – 2501

    9. Januar 2015 um 13:59 Uhr


Benutzeravatar von John Brennen
Johannes Brennen

Dies funktioniert in meiner Version von GCC, scheint aber ein totaler Hack zu sein. Es ist vielleicht nützlich in automatisch generiertem Code, wo Sie sich nicht mit der zusätzlichen Komplexität des Generierens eindeutiger Struktur-Tags befassen möchten, aber ich strenge mich an, um auch nur diese Rationalisierung zu finden.

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

Außerdem hängt es davon ab, ob typeof() im Compiler vorhanden ist – GCC und LLVM scheinen es zu unterstützen, aber ich bin mir sicher, dass viele Compiler dies nicht tun.

  • Diese Technik kann vernünftiger sein, als Sie denken. Sehen Sie, wie die Sprache D eine praktische Syntax dafür hat: https://dpaste.dzfl.pl/c5880e348812

    – Kauterit

    19. Juli 2016 um 20:52 Uhr

  • Für die meisten Anwendungsfälle können Sie verwenden decltype() in MSVC als Ersatz für typeof(). decltype() ist eigentlich Teil des C++ 11-Standards und wird daher normalerweise nicht von einfachen C-Compilern unterstützt, aber MSVC ist ein C++-Compiler, selbst wenn einfacher C-Quellcode damit kompiliert wird, es sei denn, Sie erzwingen dies durch entsprechende Parameter.

    – Mecki

    20. Februar 2019 um 13:50 Uhr

  • Ich bekomme “neue Typen dürfen nicht in einem Rückgabetyp definiert werden” mit g++, aber vielleicht in C kann anders sein als C++

    – Prokop Hapala

    26. Januar 2021 um 14:20 Uhr

  • Das typeof wird Teil des kommenden C23-Standards sein.

    – tstanisl

    17. Juli um 12:36 Uhr

Benutzeravatar von Basile Starynkevitch
Basile Starynkevitch

Kannst du wahrscheinlich nicht ausdrücklich return einen Gesamtwert aus Ihrer Funktion (es sei denn, Sie verwenden a typeof Erweiterung, um den Typ des Ergebnisses zu erhalten).

Die Moral von der Geschichte ist, dass selbst wenn Sie eine Funktion deklarieren können, die eine zurückgibt anonym structDu solltest praktisch TU das niemals.

Benennen Sie stattdessen die struct und 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 es ist im Allgemeinen ein undefiniertes Verhalten bei der Ausführung, eine Funktion ohne zu haben return (Sie könnten z. B. anrufen exit im Inneren). Aber warum sollten Sie Folgendes codieren wollen (wahrscheinlich legal)?

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

Hier ist eine Möglichkeit, anonyme Strukturen in C++14 ohne Hacks zurückzugeben, die ich gerade entdeckt habe.
(C++ 11 sollte ausreichen, nehme ich an)

In meinem Fall eine Funktion intersect() kehrt zurück std::pair<bool, Point> Das ist nicht sehr aussagekräftig, also habe ich beschlossen, einen benutzerdefinierten Typ für das Ergebnis zu erstellen.
Ich hätte es separat machen können struct aber es hat sich nicht gelohnt, da ich es nur für diesen speziellen Fall brauchen würde; Deshalb habe ich eine anonyme Struktur verwendet.

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

    // do stuff...

    return result;
}

Und jetzt statt des Hässlichen

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

Ich kann das viel besser aussehende verwenden:

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

Benutzeravatar von tstanisl
tstanisl

Sie könnten eine Struktur in der Angabe des Rückgabearguments definieren. Darüber hinaus können Sie der Kürze halber zusammengesetzte Literale verwenden, die in C99 eingeführt wurden:

#include<stdio.h>

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

int main()
{
    struct foo res = foo();
    printf("%d %d\n", res.x, res.y);
}

Drucke:

1 2

Der Code wird im pedantischen Modus für C99 ohne Warnungen kompiliert. Beachten Sie, dass der Tag-Name mit der Funktion identisch ist. Dies funktioniert, weil Namensräume für Strukturen und globale Objekte getrennt sind. Auf diese Weise minimieren Sie die Wahrscheinlichkeit eines versehentlichen Namenskonflikts. Sie können so etwas wie verwenden foo_result wenn Sie es für geeigneter halten.

Benutzeravatar von AnArrayOfFunctions
EinArrayOfFunctions

Oder Sie könnten eine unendliche Rekursion erstellen:

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

Was ich für völlig legal halte.

Benutzeravatar von Peter Mortensen
Peter Mortensen

Dies funktioniert bis zur neuesten Version von GCC. Es ist besonders nützlich, um dynamische Arrays mit Makros zu erstellen. 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 mit ein dynamisches Array erstellen können irgendein Typ, und es gibt einen Weg, sie alle zu machen. Andernfalls würden Sie am Ende viel davon verbrauchen void *‘s und dann das Schreiben von Funktionen, um die Werte mit Umwandlungen und dergleichen tatsächlich wieder herauszubekommen. Sie können all dies mit Makros abkürzen; das ist ihre Schönheit.

1392820cookie-checkWie kann ich eine anonyme Struktur in C zurückgeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy