C-Funktion Deklaration und Definition in mehr als einer Quelldatei

8

Hier ist der Inhalt von src1.c:

%Vor%

Und hier ist der Inhalt von src2.c:

%Vor%

Warum ist es nicht obligatorisch, die Funktion go in src1.c file nach dem Kompilieren unter Linux zu deklarieren?

%Vor%

Setzt der Compiler die go -Funktionsdefinition von src2.c datei über den Code der Hauptfunktion, so dass die Deklaration dann nicht erforderlich wäre?

Ich mache es so:

%Vor%

So dass jeder, der sagt, es ist möglich, die Anzahl und Arten von Argumenten in Abwesenheit des Prototyps zu übergeben, ist falsch. Sie werden in diesem Fall zu int s hochgestuft, müssen aber mit den in Definition angegebenen übereinstimmen.

Ich habe einige Tests durchgeführt und herausgefunden, dass selbst wenn es kompiliert wird ohne Fehler, es nicht richtig funktioniert.

src1:

%Vor%

sr2.c:

%Vor%

Letztendlich könnte die Antwort nur undefiniertes Verhalten sein.

Danke Maxim Skurydin

    
tautvilas 26.10.2012, 11:05
quelle

5 Antworten

7

Es ist in der Tat nicht obligatorisch, einen Prototyp für eine Funktion vor der Verwendung zu haben, aber das ist eine Eigenart der frühen Tage von C.

Wenn kein Prototyp vorhanden ist, kann der Compiler die tatsächlichen Typen, die an die Funktion übergeben oder von ihr zurückgegeben werden, nicht überprüfen. Dies kann sehr schlimm sein, wenn die Verwendung und die Deklaration nicht übereinstimmen.

Wenn der Compiler keinen Prototyp für go sieht, wenn go(b); aufgerufen wird, nimmt er an, dass er den folgenden Prototyp int go(<any number of arguments can be there>) hat. Die Standardargumentaktionen werden für die Argumente vor dem Funktionsaufruf ausgeführt. Wenn in einem anderen Übersetzungsmodul keine Funktion go vorhanden ist, erhalten Sie natürlich einen Linker-Fehler.

Ab c99 Standard:

  

6.5.2.2 Funktionsaufrufe

     

Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der   enthält keinen Prototyp, an dem Ganzzahl-Promotions ausgeführt werden   Jedes Argument und alle Argumente, die den Typ float haben, werden in promoted   doppelt. Diese werden als default argument promotions bezeichnet. Wenn die   Anzahl der Argumente ist nicht gleich der Anzahl der Parameter, die   Verhalten ist nicht definiert. Wenn die Funktion mit einem Typ definiert ist, der   enthält einen Prototyp, und entweder endet der Prototyp mit einer Ellipse   (, ...) oder die Arten der Argumente nach der Promotion sind nicht   kompatibel mit den Typen der Parameter ist das Verhalten   nicht definiert. Wenn die Funktion mit einem Typ definiert ist, der dies nicht tut   Fügen Sie einen Prototyp und die Typen der Argumente nach dem Hochstufen ein   sind nicht mit denen der Parameter nach der Förderung kompatibel, die   Das Verhalten ist nicht definiert, mit Ausnahme der folgenden Fälle:

     

- Einer wurde befördert   type ist ein vorzeichenbehafteter ganzzahliger Typ, der andere beworbene Typ ist der   entsprechenden vorzeichenlosen Integer-Typ, und der Wert ist in darstellbar   beide Typen;

     

- beide Typen sind Zeiger auf qualifizierte oder unqualifizierte   Versionen eines Zeichentyps oder void.

     

6.3.1.1 Boolean, Zeichen und ganze Zahlen

     

2 / Wenn ein int alle Werte des ursprünglichen Typs darstellen kann, der Wert   wird in einen int konvertiert; Andernfalls wird es in einen unsigned int konvertiert.   Diese werden als ganzzahlige Werbeaktionen .48) bezeichnet. Alle anderen Typen sind

Aktualisierung:

  

Setzt der Compiler die Definition der go-Funktion aus der obigen src2.c-Datei   der Code der Hauptfunktion, so dass die Deklaration dann nicht wäre   benötigt?

Nein, es bringt nichts. Ein Zitat des obigen Standards besagt, dass kein Prototyp notwendig ist. Jede Datei wird unabhängig kompiliert, also wenn src1.c kompiliert wird, weiß sie nichts über src2.c und eine go Funktionsdefinition darin.

  

Also jeder, der sagt, es ist möglich, die Nummer und die Arten zu übergeben   von Argumenten in Abwesenheit von Prototyp ist falsch. Sie werden befördert zu   intS in diesem Fall, muss aber mit denen in übereinstimmen   Definition.

Es ist möglich und ich habe nach einer systemweiten Änderung ein paar obskure Bugs erlebt, die ohne irgendwelche Warnungen aus irgendeinem Grund gut kompiliert wurden (eigentlich ist es ein undefiniertes Verhalten). Da jede * .c-Datei unabhängig voneinander kompiliert wird, gibt es jetzt die Möglichkeit, die Anzahl der Argumente und ihre Arten von go -Funktion, die in einer anderen Übersetzungseinheit definiert sind, zu überprüfen. Wenn die Funktion mehr Argumente benötigt, die Sie angegeben haben, werden die "unbenutzten" Argumente mit einigen zufälligen Daten gefüllt. Sie sollten beachten, dass, wenn Argumente nicht übereinstimmen - es ist undefined Verhalten, was bedeutet, dass alles passieren kann.

    
Maksim Skurydzin 26.10.2012, 11:10
quelle
0

Zunächst sollten Funktionsdeklarationen in Header-Dateien eingefügt werden. Jetzt eine Antwort auf deine Frage:
Wenn Sie beide Dateien kompilieren, findet der Linker zum Zeitpunkt des Linkens die Symboldefinition von go () in src2.o und löst somit die Symbolreferenz in der ausführbaren Datei auf. Dies ist der Grund, warum Ihr Programm funktioniert.

Sie versuchen, sizeof() zu verwenden, das ein Kompilierzeitoperator ist, und es würde daher 1 ausgeben, da Sie es für einen Charakter ausgeben.
Sie übergeben auch einen Ganzzahlwert & gt; 255 an eine Variable char, dies würde einen Überlauf verursachen und t würde 1789modulo255 speichern.

    
Manik Sidana 26.10.2012 11:09
quelle
0

Wenn Sie mit diesen beiden Quelldateien eine ausführbare Datei erstellen, wird die endgültige ausführbare Datei vorhanden sein Definition von go() also nicht nötig.

Aber es ist besser, declaration in ein header file zu setzen und diese Header-Datei dann in beide Quelldateien einzufügen

Hier ist Header-Datei someheader.h

%Vor%

schließe es jetzt so ein

%Vor%

in src1.c und src2.c

    
Omkant 26.10.2012 11:08
quelle
0

Standardmäßig

go () wird int go() sein. d. h. gibt int zurück und akzeptiert eine beliebige Anzahl von Argumenten . Ihre aktuelle Funktion entspricht also dem Standardfunktionstyp.

    
Jeyaram 26.10.2012 11:07
quelle
0

Wenn der Compiler go() in str1.c sieht, nimmt er an, dass die Funktion an anderer Stelle definiert ist. Es ist nur zu der Verknüpfungszeit, nach der der Linker sucht die Definition von go() .

Ich denke, Sie kompilieren die beiden Dateien separat und verknüpfen sie miteinander, was in Ordnung ist. Weil zur Verbindungszeit die Definition von go() existiert.

Wenn Sie versuchen, str1.c ( gcc str1.c im Gegensatz zu gcc -c str1.c ) separat zu kompilieren, erhalten Sie den Fehler, dass go() nicht vom Linker gefunden wird.

UPDATE:

Selbst die implizite Deklaration durch den Compiler ist nicht standardkonform (seit C99) .

Technisch gesehen sollte jede Funktion einen Prototyp unabhängig von ihrem Rückgabetyp haben, wenn sie aufgerufen wird, bevor der Compiler seine Definition sehen kann. Die implizite Deklaration einer int-Rückgabefunktion ist nicht mehr gültig (gültig in C89) und wurde seit C99 (und C11) entfernt.

Obwohl die meisten Compiler immer noch nur eine Warnung ausgeben, ist dies kein Fehler. Aber wenn ein Compiler es ablehnt, zu kompilieren, weil der Funktionsprototyp nicht vorhanden ist, dann kann man sich nicht beschweren, da es nicht standardkonform ist.

    
P.P. 26.10.2012 11:10
quelle

Tags und Links