Wie kann ich eine anonyme Struktur in C zurückgeben?
Lesezeit: 8 Minuten
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
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.
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
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.
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
Basile Starynkevitch
Kannst du wahrscheinlich nicht ausdrücklichreturn 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 anonymstructDu solltest praktischTU 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;
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:
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.
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.
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.
13928200cookie-checkWie kann ich eine anonyme Struktur in C zurückgeben?yes
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