10 Stimmen

Unterschied in der Verknüpfung zwischen C und C++?

Ich habe die bestehenden Fragen zur externen/internen Verknüpfung hier auf SO gelesen. Meine Frage ist eine andere - was passiert, wenn ich mehrere Definitionen der gleichen Variable mit externer Verknüpfung in verschiedenen Übersetzungseinheiten habe unter C y C++ ?

Zum Beispiel:

/*file1.c*/

typedef struct foo {
    int a;
    int b;
    int c;
} foo;

foo xyz;

/*file2.c*/

typedef struct abc {
    double x;
} foo;

foo xyz;

Mit Dev-C++ und als C-Programm lässt sich das obige Programm perfekt kompilieren und verknüpfen, wohingegen es einen Fehler wegen mehrfacher Neudefinition gibt, wenn das gleiche als C++-Programm kompiliert wird. Warum sollte es unter C funktionieren und was ist der Unterschied zu C++? Ist dieses Verhalten undefiniert und Compiler-abhängig? Wie "schlimm" ist dieser Code, und was sollte ich tun, wenn ich ihn umstrukturieren möchte (ich bin auf eine Menge alten Code gestoßen, der so geschrieben ist)?

4voto

CB Bailey Punkte 693084

Sowohl C als auch C++ haben eine "Ein-Definitions-Regel", die besagt, dass jedes Objekt nur einmal in einem Programm definiert werden darf. Verstöße gegen diese Regel verursachen undefiniertes Verhalten was bedeutet, dass beim Kompilieren eine Diagnosemeldung erscheinen kann oder auch nicht.

Es gibt einen sprachlichen Unterschied zwischen den folgenden Deklarationen auf Dateiebene, der aber nicht direkt das Problem mit Ihrem Beispiel betrifft.

int a;

In C ist dies eine vorläufige Definition. Sie kann mit anderen vorläufigen Definitionen in derselben Übersetzungseinheit zu einer einzigen Definition zusammengefügt werden. In C++ ist es immer eine Definition (Sie müssen extern um ein Objekt zu deklarieren, ohne es zu definieren) und alle nachfolgenden Definitionen des gleichen Objekts in der gleichen Übersetzungseinheit sind ein Fehler.

In Ihrem Beispiel haben beide Übersetzungseinheiten eine (widersprüchliche) Definition von xyz aus ihren vorläufigen Definitionen.

2voto

hlovdal Punkte 24350

Dies wird durch die Namensmanipulation von C++ verursacht. Von Wikipedia :

Die ersten C++-Compiler waren als Übersetzer für C-Quellcode implementiert Code implementiert, der dann von einem einem C-Compiler in Objektcode kompiliert wurde; deshalb mussten die Symbolnamen den C-Bezeichnungsregeln C-Bezeichner-Regeln entsprechen. Noch später, mit dem Aufkommen von Compilern, die Maschinencode oder Assembler direkt erzeugten, unterstützte der Linker des Systems im Allgemeinen keine C++-Symbole unterstützt, und das Mangling war weiterhin erforderlich.

In Bezug auf Kompatibilität :

Um Compiler-Anbietern die Möglichkeit zu geben größere Freiheit zu geben, hat das C++-Standardisierungs Ausschuss beschlossen, die Implementierung Implementierung der Namensumwandlung vorzuschreiben, Ausnahmebehandlung und andere implementierungsspezifischen Funktionen vorzuschreiben. Die Nachteil dieser Entscheidung ist, dass Objektcode, der von verschiedenen Compilern erzeugter Objektcode voraussichtlich inkompatibel ist. Es gibt jedoch Standards von Drittanbietern für bestimmte Maschinen oder Betriebssysteme, die versuchen, Compiler für diese Plattformen zu standardisieren diese Plattformen zu standardisieren (z. B. C++ ABI[18]); einige Compiler übernehmen einen sekundären Standard für diese Elemente.

Von http://www.cs.indiana.edu/~welu/notes/node36.html wird das folgende Beispiel gegeben:


Zum Beispiel für den folgenden C-Code

int foo(double*);
double bar(int, double*);

int foo (double* d) 
{
    return 1;
}

double bar (int i, double* d) 
{
    return 0.9;
}

Seine Symboltabelle wäre (durch dump -t )

[4]  0x18        44       2     1   0   0x2 bar
[5]  0x0         24       2     1   0   0x2 foo

Wenn dieselbe Datei in G++ kompiliert wird, würde die Symboltabelle wie folgt aussehen

[4]  0x0         24       2     1   0   0x2 _Z3fooPd
[5]  0x18        44       2     1   0   0x2 _Z3bariPd

_Z3bariPd bedeutet eine Funktion, deren Name bar ist und deren erstes Argument eine ganze Zahl und das zweite Argument ein Zeiger auf double ist.


1voto

tobing Punkte 19

C++ erlaubt es nicht, ein Symbol mehr als einmal zu definieren. Ich bin mir nicht sicher, was der C-Linker tut. Eine gute Vermutung wäre, dass er einfach beide Definitionen auf dasselbe Symbol abbildet, was natürlich schwere Fehler verursachen würde.

Für die Portierung würde ich versuchen, den Inhalt der einzelnen C-Dateien in anonyme Namensräume zu packen, was die Symbole im Wesentlichen anders macht und lokal in der Datei, so dass sie nicht mit dem gleichen Namen anderswo kollidieren.

0voto

Michael J Punkte 7247

Das C-Programm erlaubt dies und behandelt den Speicher ein wenig wie eine Vereinigung. Es wird ausgeführt, liefert aber möglicherweise nicht das, was Sie erwartet haben.

Das C++-Programm (das stärker typisiert ist) erkennt das Problem korrekt und fordert Sie auf, es zu beheben. Wenn Sie wirklich eine Union wollen, deklarieren Sie sie als eine. Wenn Sie zwei getrennte Objekte wollen, begrenzen Sie ihren Geltungsbereich.

0voto

Potatoswatter Punkte 130562

Sie haben die Die Regel der einen Definition . Offensichtlich hat Ihr Programm einen Fehler, denn

  • Es kann nur ein Objekt mit dem Namen foo sobald das Programm verknüpft ist.
  • Wenn eine Quelldatei alle Header-Dateien enthält, sieht sie zwei Definitionen von foo .

C++-Compiler können Nr. 1 durch "name mangling" umgehen: Der Name Ihrer Variablen im verknüpften Programm kann sich von dem von Ihnen gewählten Namen unterscheiden. In diesem Fall ist dies zwar nicht erforderlich, aber wahrscheinlich hat Ihr Compiler das Problem so erkannt. #2 bleibt jedoch bestehen, also können Sie das nicht tun.

Wenn Sie den Sicherheitsmechanismus wirklich aushebeln wollen, können Sie das Mangeln auf diese Weise deaktivieren:

extern "C" struct abc foo;

andere Datei

extern "C" struct foo foo;

extern "C" weist den Linker an, die C ABI-Konventionen zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X