2013-03-24 3 views
6

CWie funktioniert das Referenzieren, wenn zwei .c-Dateien globale Variablen mit denselben Symbolen, aber unterschiedlichen Typen haben?

Sagen, ich habe folgende C-Module:

MODUL 1

#include <stdio.h> 
int x; 
int main(){ 
    foo(); 
    printf("%i\n",x); 
    return 0; 
} 

MODUL 2

double x; 
void foo(){ 
    x = 3.14; 
} 

Meine Frage ist: Was tut der Linker in diese tun Fall? In dem Lehrbuch, das ich lese, steht, dass der Compiler nur eine von zwei schwachen globalen Variablen für die Linker-Symboltabelle auswählt. Welches dieser beiden ist gewählt? Oder sind beide ausgewählt? Wenn ja warum? Vielen Dank.

Antwort

5

C sagt, es ist undefiniertes Verhalten.

(C99, 6.9p5) „Wenn ein Identifikator mit externer Bindung erklärt in einem Ausdruck (ausgenommen als Teil des Operanden von einem Operator sizeof Ergebnis dessen ist eine ganzzahlige Konstante) verwendet wird, irgendwo in dem gesamten Programm, es wird genau ein externe Definition für den Identifizierer sein, sonst wird es nicht mehr als ein“

undefiniert Verhalten bedeutet ein Linker den Verknüpfungsvorgang in Anwesenheit von mehreren externen Objektdefinitionen abbrechen kann.

Jetzt Linker sind schön (oder böse, können Sie wählen) und in der Regel Standarderweiterungen müssen mehrere externe Objektdefinitionen handhaben und in einigen Fällen nicht scheitern. Wenn Sie gcc und ld von Binutils verwenden, erhalten Sie einen Fehler, wenn Ihre beiden Objekte explizit initialisiert werden. Zum Beispiel haben Sie int x = 0; in der ersten Übersetzungseinheit und double x = 0.0;.

Andernfalls, wenn eines der externen Objekte nicht explizit initialisiert wird (die Situation in Ihrem Beispiel) gcc wird die beiden Objekte in ein Symbol stillschweigend kombinieren. Sie können den Linker dennoch bitten, eine Warnung zu melden, indem Sie ihm die Option --warn-common übergeben.

Zum Beispiel bei der Verknüpfung der Module:

gcc -Wl,--warn-common module1.o module2.o

den Verknüpfungsvorgang abgebrochen zu erhalten, können Sie den Linker anfordern können alle Warnungen als Fehler mit --fatal-warnings Option (-Wl,--fatal-warnings,--warn-common) zu behandeln.

Eine weitere Möglichkeit, den Verknüpfungsprozess abgebrochen zu bekommen, ist die Verwendung der Compileroption -fno-common, wie in der Antwort von @teppic erläutert. -fno-common verbietet den externen Objekten, einen gemeinsamen Symboltyp bei der Kompilierung zu erhalten. Wenn Sie dies sowohl für das Modul als auch für den Link tun, erhalten Sie auch den Linkerdefinitionsfehler.

gcc -Wall -fno-common -c module1.c module2.c

gcc module1.o module2.o

+0

Also, in diesem Fall gibt es nur ein "x" in der Symboltabelle? Ich kann einige seltsame Bugs sehen, die daraus resultieren. Thx – amorimluc

+0

@amorimluc in Ihrem Fall Chancen, dass Sie möchten, erhalten einen Fehler von der Linker oder ein einzelnes Objekt in der endgültigen binären und Ihr Programm wird undefiniertes Verhalten aufrufen. – ouah

+0

Ich dachte ich würde, aber es kompiliert und verbindet gut. Und x wird als int gedruckt, aber mit dem Bitmuster des 3.14 float. Dies passiert wahrscheinlich nicht viel im wirklichen Leben, aber es wäre sicher schwer, diesen subtilen Fehler in einem großen Programm zu finden ... – amorimluc

1

Wenn die Implementierung mehr externen Definitionen unterstützt, werden Sie mit einem Objekt am Ende, die in jedem Modul zu jedem Typ effektiv gegossen sind, wie in einer Art impliziter Vereinigung variabel. Die Speichermenge für den größeren Typ wird zugewiesen, und beide werden sich wie externe Deklarationen verhalten.

Wenn Sie mit clang oder gcc kompilieren, verwenden Sie die Option -fno-common, um einen Fehler zu verursachen.

Hier ist der Abschnitt von dem gcc Handbuch:

 In C code, controls the placement of uninitialized global 
     variables. Unix C compilers have traditionally permitted multiple 
     definitions of such variables in different compilation units by 
     placing the variables in a common block. This is the behavior 
     specified by -fcommon, and is the default for GCC on most targets. 
     On the other hand, this behavior is not required by ISO C, and on 
     some targets may carry a speed or code size penalty on variable 
     references. The -fno-common option specifies that the compiler 
     should place uninitialized global variables in the data section of 
     the object file, rather than generating them as common blocks. 
     This has the effect that if the same variable is declared (without 
     "extern") in two different compilations, you will get a multiple- 
     definition error when you link them. 

Diese Option effektiv erzwingt strenge ISO C Einhaltung in Bezug auf mehrere Definitionen.

Dieses Verhalten wird allgemein für externe Variablen des Typs selbe akzeptiert. Wie das GCC-Handbuch feststellt, unterstützen die meisten Compiler dies, und (sofern die Typen gleich sind) definiert der C99-Standard seine Verwendung als Erweiterung.