2016-05-09 20 views
3

Die C11 Standards sprechen über die Verknüpfung von Bezeichnern, aber es gibt keine offensichtliche Diskussion über die Regeln der Verknüpfung von Übersetzungseinheiten. Meine Frage wird aufgeworfen, indem ich zwei einfache Beispiele unter Verwendung von Klängen zusammenstelle.Welche Regeln gelten für die Verknüpfung von Übersetzungseinheiten in C11?

Hier ist mein erstes Beispiel, die zwei Erklärungen der gleichen Funktion hat, aber mit nicht kompatibelen Typen:

//testall.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

char myfun(char*c){ 
    return *c; 
} 

Dann laufe ich den Befehl: $ Klirren -std = c11 testall.c
Und Klirren Berichte ein Fehler:

testall.c:9:10: error: conflicting types for 'myfun'
char myfun(char*c){
^
testall.c:2:17: note: previous declaration is here
extern char myfun(void*);
^
1 error generated.

Ich verstehe diesen Fehler, weil der Zeiger und der Zeiger Zeiger auf char sind inkompatible Typen.
Was mich verwirrt ist, dass, wenn ich die beiden Erklärungen in zwei verschiedene Übersetzungseinheiten trennen und sie dann verbinden in einem, Klirren keine Fehler meldet:

//test.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

// mylib.c 
char myfun(char*c){ 
    return *c; 
} 

Dann laufe ich diesen Befehl: $ Klirren -std = c11 test.c mylib.c.
clang kompiliert und verknüpft die beiden Übersetzungseinheiten, ohne einen Fehler oder eine Warnung zu melden.

Ich dachte, dass die Verknüpfung von zwei Übersetzungseinheiten den Regeln in Abschnitt 6.2.2 Verknüpfungen von Identifikatoren der C11-Standards folgt. Aber es scheint, dass dies nicht der Fall ist. Kann mir jemand helfen, es zu klären?

+0

Nur weil Sie keine Diagnose bekommen, bedeutet das nicht, dass der Code legal ist! Herkömmlicherweise betrachtet ein Compiler nur eine Übersetzungseinheit gleichzeitig (so dass die Nichtübereinstimmung beim Kompilieren nicht erkannt werden konnte), und Typinformationen werden nicht in die Objektdatei übertragen, sodass der Linker die Nichtübereinstimmung nicht erkennen kann entweder. –

+0

@NateEldredge Versuchen Sie, jeweils nur eine Datei auszuführen, anstatt beide in der Befehlszeile anzugeben. Doing 'gcc a.c b.c' kompiliert und verbindet in einem Schritt. C hat keinen Namen Mangling, so dass die Namen der Funktion für den Linker genau gleich aussehen. Was nun, wenn Sie drei Objektdateien mit 'myfun' haben? Sie erhalten einen Funktionsneudefinitionsfehler. Also klar, was du gesagt hast, kann nicht wahr sein. –

+0

Obwohl 'gcc a.c b.c 'sowohl den Compiler als auch den Linker ausführt, handelt es sich um zwei separate Durchläufe, die keine anderen Informationen als die Objektdatei gemeinsam nutzen. Der Linker kann mehrere ** Definitionen ** desselben Symbols erkennen. Aber in Ihrem Beispiel ist die Funktion 'myfun' nur einmal ** in' mylib.c' definiert. Die Datei 'test.c' enthält eine ** Erklärung ** von' myfun', aber keine ** Definition **. Daher kein Fehler vom Linker. –

Antwort

2

In Ihrem Beispiel mit zwei separaten Dateien ist das Verhalten nicht definiert. Hier ist meine „Fall“, basierend auf dem C11 Standard:

C11 6.2.2 (4):

For an identifier declared with the storage-class specifier extern ... If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

In test.c hat myfun externe Bindung, weil sie mit extern und keine vorherige Anmeldung erklärt wird, sichtbar.

C11 6.2.2 (5):

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern.

In mylib.c, myfun wird ohne Speicherklassen-Bezeichner deklariert, daher wird es, als ob es mit extern erklärt wurden, deshalb hat es eine externe Bindung in dieser Übersetzungseinheit. .

C11 6.9.1 (7) [Funktionsdefinitionen]:

If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit

Daraus ergibt sich die Definition von myfun in mylib.c ist auch eine Erklärung von myfun (falls Sie irgendwelche Zweifel hatte).

C11 6.2.2 (2):

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function.

daher die beiden myfun s die gleiche Funktion bezeichnen.

C11 6.2.7 (2):

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

Die beiden Erklärungen von myfun haben inkompatible Typen (I zeigen konnte, dass, wenn Sie wollen, aber es ist, warum Clang in der Single-File beschwerte Fall.) Daher ist das Verhalten des gesamten Programms undefiniert.

Beachten Sie, dass 6.2.7 (2) keine Einschränkung ist, daher ist Clang nicht erforderlich, um eine Diagnose zu erstellen, wenn sie verletzt wird. Allerdings in der einzigen Datei Fall gibt es eine tatsächliche Beschränkung verletzt, wie die folgenden unter der Überschrift "Constraints" auftritt:

C11 6.7 (4):

All declarations in the same scope that refer to the same object or function shall specify compatible types.

So Clang muss die Ausgabe von Diagnose in diesem Fall.

3

Dies ist nur undefined Verhalten. C11 sagt nichts über Linker oder wie sie mehrere Übersetzungseinheiten kombinieren. In der Praxis ist dies kein Problem, da es eine Header-Datei mit Funktionsdeklaration für myfun() geben würde, die beide diese C-Dateien enthält.

+0

Bedeutet Ihr "undefiniertes Verhalten", dass irgendetwas wie die C11-Standards passieren könnte? Oder meinen Sie, dass Linker einfach außerhalb des Geltungsbereichs der C11-Standards liegen? –

+0

Ich meine das ehemalige und das letztere ist auch wahr. Im Grunde tun Linker (zumindest die Unix- und GNU-Linker, die ich benutzt habe) keine Typprüfung. Eine Diagnose ist also nicht einfach. Aber in Ihrem speziellen Beispiel wird es auch auf allen Systemen wie erwartet funktionieren, da 'char *' und 'void *' die gleiche Darstellung haben müssen. –