Gibt ein nicht definiertes strukturdefiniertes undefiniertes Verhalten zurück?

8

Ich habe den folgenden Code (Include-Wächter aus Gründen der Einfachheit weggelassen):

= foo.hpp =

%Vor%

= main.cpp =

%Vor%

= foo_generator.hpp =

%Vor%

= foo_generator.cpp =

%Vor%

Dieser Code, so wie er aussieht, ist völlig in Ordnung ohne jede Warnung. Wenn mein Verständnis stimmt, sollte es ausgeben:

%Vor%

Aber stattdessen wird zufällig angezeigt:

%Vor%

Oder stürzt einfach ab.

Wenn ich die Vorwärtsdeklaration von FOO in foo_generator.hpp durch #include "foo.hpp" ersetze, funktioniert es.

Hier ist meine Frage: Führt die Rückgabe einer nach vorne deklarierten Struktur zu undefiniertem Verhalten? Oder was kann möglicherweise schiefgehen?

Verwendeter Compiler: MSVC 9.0 und 10.0 (beide zeigen das Problem)

    
ereOn 30.11.2010, 10:30
quelle

3 Antworten

7

Das sollte gemäß 8.3.5.6 in Ordnung sein: "Der Typ eines Parameters oder der Rückgabetyp für eine Funktionsdeklaration, die keine Definition ist, kann ein unvollständiger Klassentyp sein."

    
visitor 30.11.2010, 10:56
quelle
3

Ich denke, ich habe das gleiche Problem. Dies geschieht mit kleinen Rückgabewerttypen und die Reihenfolge der Header-Including . Um dies zu vermeiden, verwenden Sie keine Rückgabetyp-Vorwärtsdeklaration oder Header in der gleichen Reihenfolge .

Für eine mögliche Erklärung sieh dir das an:

func.h

%Vor%

func.cpp

%Vor%

foo.h

%Vor%

Beachten Sie, dass das ganze Foo in ein einzelnes CPU-Register passt.

func.asm (MSVS 2005)

%Vor%

Wenn func () angegeben wird Foo's Größe ist unbekannt. Es weiß nicht, wie Foo zurückgegeben werden könnte. Also erwartet func () den Zeiger, um den Wertspeicher als seinen Parameter zurückzugeben. Hier ist es _ $ ReturnUdt $. Der Wert von Foo () wird dort kopiert.

Wenn wir die Reihenfolge der Header in func.cpp ändern, erhalten wir:

func.asm

%Vor%

Nun weiß der Compiler, dass Foo klein genug ist, so dass es über das Register zurückgegeben wird und keine zusätzlichen Parameter benötigt werden.

main.cpp

%Vor%

Beachten Sie, dass hier die Größe von Foo bekannt ist, wenn func () deklariert ist.

main.asm

%Vor%

Der Compiler geht davon aus, dass func () den Wert über das Register zurückgibt. Es gibt keinen Zeiger auf die temporäre Position, um den Rückgabewert zu speichern. Aber wenn func () den Zeiger erwartet, schreibt es in den Speicher, der den Stapel korrumpiert.

Lassen Sie uns die Reihenfolge der Header ändern, also geht func.h zuerst.

main.asm

%Vor%

Der Compiler übergibt den Zeiger, den func () erwartet, so dass keine Stapelbeschädigungen auftreten.

Wenn die Größe von Foo größer als 2 Ganzzahlen wäre, würde der Compiler immer den Zeiger übergeben.

    
fil 14.03.2011 13:25
quelle
1

Es funktioniert gut für mich unter GCC. Ich weiß nicht, warum es nicht wäre, da foo.hpp vor foo_generator.hpp enthalten ist.

    
Matthew Flaschen 30.11.2010 10:37
quelle