Vorläufige Definitionen in C und Verlinkung

Lesezeit: 6 Minuten

Benutzeravatar von Pascal Cuoq
Pascal Cuoq

Stellen Sie sich das C-Programm vor, das aus zwei Dateien besteht.

f1.c:

int x;

f2.c:

int x=2;

Meine Lektüre von Absatz 6.9.2 des dem C99-Standard ist, dass dieses Programm abgelehnt werden sollte. In meiner Interpretation von 6.9.2, variabel x ist vorläufig definiert in f1.caber diese vorläufige Definition wird am Ende der Übersetzungseinheit zu einer tatsächlichen Definition und sollte sich daher (meiner Meinung nach) so verhalten, als ob f1.c enthielt die Definition int x=0;.

Bei allen Compilern (und vor allem Linkern), die ich ausprobieren konnte, passiert das nicht. Alle Kompilierungsplattformen, die ich ausprobiert habe, verknüpfen die beiden oben genannten Dateien und den Wert von x ist in beiden Dateien 2.

Ich bezweifle, dass dies versehentlich passiert oder nur als “einfache” Funktion zusätzlich zu dem, was der Standard erfordert, bereitgestellt werden soll. Wenn Sie darüber nachdenken, bedeutet dies, dass der Linker spezielle Unterstützung für globale Variablen bietet, die keinen Initialisierer haben, im Gegensatz zu denen, die explizit mit Null initialisiert wurden. Jemand sagte mir, dass die Linker-Funktion möglicherweise trotzdem erforderlich ist, um Fortran zu kompilieren. Das wäre eine vernünftige Erklärung.

Irgendwelche Gedanken dazu? Andere Interpretationen der Norm? Namen von Plattformen, auf denen Dateien f1.c und f2.c sich weigern, miteinander verbunden zu werden?

Hinweis: Dies ist wichtig, da die Frage im Kontext der statischen Analyse auftritt. Wenn sich die beiden Dateien auf einer Plattform weigern, verlinkt zu werden, sollte sich der Analysator beschweren, aber wenn jede Kompilierungsplattform dies akzeptiert, gibt es keinen Grund, davor zu warnen.

  • Danke für das Teilen. niemals zu alt zum Lernen

    – Adrian

    29. September 2009 um 10:17 Uhr

  • Der Compiler muss Dinge nur ablehnen (dh warnen oder Fehler melden), wenn Sie Dinge in einem Einschränkungsabsatz verletzen. Die Einschränkung, dass Sie keine zwei externen Definitionen für Ihre Dinge haben dürfen, ist ein “soll”. außen ein Zwangsabsatz. Verstoß gegen irgendwelche soll außerhalb einer Einschränkung führt automatisch zu undefiniertem Verhalten in C – das gibt dem Compiler die Möglichkeit, es so zu behandeln, wie er es möchte.

    – Johannes Schaub – litb

    29. September 2009 um 11:35 Uhr

  • @litb Das ist ein interessanter Punkt. Der erwähnte statische Analysator versucht, wenn möglich, keine /etablierten/ Programmierpraktiken zu kennzeichnen, selbst wenn sie nicht durch den Standard definiert sind. Ich denke, wir werden uns hier entscheiden, nicht zu warnen, da auf einer Plattform, auf der diese mehreren Definitionen nicht unterstützt werden, wahrscheinlich Sie würden zur Verbindungszeit und nicht zur Laufzeit zu einem Fehler führen. PS: Ich weiß, was “undefiniert” bedeutet, aber jede zusätzliche Analyseoption macht den Analysator ein bisschen weniger brauchbar, und das muss gegen die Gewinne abgewogen werden. Daher der Teil “Namen von Plattformen, auf denen …” der Frage

    – Pascal Cuoq

    29. September 2009 um 11:58 Uhr

  • Aktuelle gcc-Versionen verwenden -fno-common standardmäßig. Dann erhalten Sie einen Linker-Fehler, auch wenn Sie nur einen haben int x; ohne Initialisierung in f2.c. Das Zusammenführen vorläufiger Definitionen über Kompilierungseinheiten hinweg ist meiner Meinung nach schlecht. Es wird zu Fehlern führen. Das Schlüsselwort extern existiert jetzt, um die Dinge richtig zu machen.

    – Sven

    23. Januar 2021 um 3:30 Uhr


Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Siehe auch Was sind externe Variablen in C. Dies wird im C-Standard im informativen Anhang J als allgemeine Erweiterung erwähnt:

J.5.11 Mehrere externe Definitionen

Es kann mehr als eine externe Definition für den Bezeichner eines Objekts geben, mit oder ohne ausdrückliche Verwendung des Schlüsselworts extern; wenn die Definitionen nicht übereinstimmen oder mehr als eine initialisiert wird, ist das Verhalten undefiniert (6.9.2).

Warnung

Wie @litb hier betont und wie in meiner Antwort auf die Querverweisfrage angegeben, führt die Verwendung mehrerer Definitionen für eine globale Variable zu undefiniertem Verhalten, was die Art und Weise des Standards ist, zu sagen, dass “alles passieren kann”. Eines der Dinge, die passieren können, ist, dass sich das Programm so verhält, wie Sie es erwarten; und J.5.11 sagt ungefähr: “Sie könnten öfter Glück haben, als Sie verdienen”. Aber ein Programm, das auf mehrere Definitionen einer externen Variablen angewiesen ist – mit oder ohne das explizite Schlüsselwort „extern“ – ist kein streng konformes Programm und es wird nicht garantiert, dass es überall funktioniert. Äquivalent: es enthält a Insekt die sich zeigen kann oder auch nicht.

  • Während ich speziell nach nicht-externen Variablen gefragt habe, gibt es in diesem Absatz tatsächlich einen interessanten Leckerbissen zur Klarstellung. Danke für den Hinweis. Das liebe ich an Standards…

    – Pascal Cuoq

    29. September 2009 um 6:02 Uhr

  • Da sich die beiden Variablen beide im Dateibereich befinden und nicht statisch sind (sie müssen so sein, damit überhaupt ein Problem auftritt), sind sie beide ‘extern’ – mit oder ohne explizite Verwendung des Schlüsselworts extern.

    – Jonathan Leffler

    29. September 2009 um 6:10 Uhr

  • Um ganz klar zu sagen, ob es erlaubt ist oder nicht: Nein, es ist ein undefiniertes Verhalten in C. Es ist wie beim Tun a[10] = 0; selbst wenn a ist ein int a[1];, was auch als gemeinsame Erweiterung erlaubt war und ist (bevor wir flexible Array-Mitglieder hatten). Ich denke, es sollte klar darauf hingewiesen werden, dass dies formal ein undefiniertes Verhalten ist, zusätzlich zu dem definierten Verhalten auf einigen Plattformen.

    – Johannes Schaub – litb

    29. September 2009 um 11:31 Uhr

  • @Jonathan, es tut mir leid, wenn ich mit meinen UB-Kommentaren ein bisschen nervig bin 🙂 Ich dachte nur, der Fragesteller könnte denken, wenn er “allgemeine Erweiterung” hört, dass der C-Standard es Programmen irgendwie erlaubt, dies zu tun und streng konform zu bleiben 🙂 + Ich habe Sie natürlich behandelt

    – Johannes Schaub – litb

    29. September 2009 um 11:38 Uhr

  • Beachten Sie, dass ein extern Schlüsselwort macht es zu einer Deklaration und überhaupt nicht zu einer Definition, daher gibt es keine Möglichkeit, “mehrere Definitionen mit einem expliziten extern Stichwort”

    – Chris Dodd

    1. Juni 2012 um 0:28 Uhr

Benutzeravatar von olovb
olovb

Es gibt eine sogenannte “allgemeine Erweiterung” des Standards, bei der das mehrfache Definieren von Variablen zulässig ist, solange die Variable nur einmal initialisiert wird. Sehen http://c-faq.com/decl/decldef.html

Die verlinkte Seite besagt, dass dies für Unix-Plattformen relevant ist – ich denke, es ist dasselbe für c99 wie für c89 -, obwohl es möglicherweise von mehr Compilern übernommen wurde, um eine Art Defacto-Standard zu bilden. Interessant.

Benutzeravatar von Pascal Cuoq
Pascal Cuoq

Dies soll meine Antwort auf einen Kommentar von olovb verdeutlichen:

Ausgabe von nm für eine aus “int x;” kompilierte Objektdatei. Auf dieser Plattform wird Symbolen ein ‘_’ vorangestellt, d. h. die Variable x erscheint als _x.

00000000 T _main
         U _unknown
00000004 C _x
         U dyld_stub_binding_helper

Ausgabe von nm für eine Objektdatei, die aus “int x=1;” kompiliert wurde

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

Ausgabe von nm für eine Objektdatei, die aus “int x=0;” kompiliert wurde

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

Ausgabe von nm für eine Objektdatei, die aus “extern int x;” kompiliert wurde

00000000 T _main
         U _unknown
         U dyld_stub_binding_helper

BEARBEITEN: Ausgabe von nm für eine Objektdatei, die aus “extern int x;” kompiliert wurde wobei x tatsächlich in einer der Funktionen verwendet wird

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper

  • Falls jemand mit der Ausgabe von nm nicht vertraut ist: D ist definiert. U ist undefiniert. und von man nm – “C” Das Symbol ist üblich. Gemeinsame Symbole sind nicht initialisierte Daten. Beim Verknüpfen können mehrere gemeinsame Symbole mit demselben Namen erscheinen. Wenn das Symbol irgendwo definiert ist, werden die allgemeinen Symbole als undefinierte Referenzen behandelt.

    – Falaina

    29. September 2009 um 7:43 Uhr

  • Danke für die Klarstellung, Pascal.

    – olovb

    30. September 2009 um 20:27 Uhr

1394970cookie-checkVorläufige Definitionen in C und Verlinkung

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

Privacy policy