wake-up-neo.com

Warum kompiliert eine Funktion ohne Parameter (im Vergleich zur eigentlichen Funktionsdefinition)?

Ich bin gerade auf den C-Code von jemandem gestoßen, bei dem ich mich nicht sicher bin, warum er kompiliert wird. Es gibt zwei Punkte, die ich nicht verstehe.

Erstens hat der Funktionsprototyp keine Parameter im Vergleich zur tatsächlichen Funktionsdefinition. Zweitens hat der Parameter in der Funktionsdefinition keinen Typ.

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

Warum funktioniert das? Ich habe es in einigen Compilern getestet und es funktioniert einwandfrei.

384
AdmiralJonB

Alle anderen Antworten sind richtig, aber nur für Vervollständigung

Eine Funktion wird folgendermaßen deklariert:

  return-type function-name(parameter-list,...) { body... }

return-type ist der Variablentyp, den die Funktion zurückgibt. Dies kann kein Array- oder Funktionstyp sein. Wenn nicht angegeben, wird int als angenommen.

Funktionsname ist der Name der Funktion.

Parameterliste ist die Liste der Parameter, die die Funktion durch Kommas getrennt nimmt. Wenn keine Parameter angegeben sind, nimmt die Funktion keine an und sollte mit einer leeren Klammer oder mit dem Schlüsselwort void definiert werden. Befindet sich in der Parameterliste kein Variablentyp vor einer Variablen, wird int angenommen. Arrays und Funktionen werden nicht an Funktionen übergeben, sondern automatisch in Zeiger konvertiert. Wenn die Liste mit einem Auslassungszeichen (, ...) abgeschlossen wird, gibt es keine festgelegte Anzahl von Parametern. Hinweis: Mit dem Header stdarg.h können Sie bei Verwendung einer Ellipse auf Argumente zugreifen.

Und noch einmal der Vollständigkeit halber. Aus C11-Spezifikation 6: 11: 6 (Seite: 179)

Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Prototypformat-Parameter-Typdeklaratoren) ist eine veraltete Funktion .

270
Krishnabhadra

In C func() können Sie beliebig viele Argumente übergeben. Wenn Sie keine Argumente wollen, müssen Sie als func(void) deklarieren. Der Typ, den Sie an Ihre Funktion übergeben, ist, falls nicht anders angegeben, standardmäßig int.

158
Tony The Lion

int func(); ist eine veraltete Funktionsdeklaration aus den Tagen ohne C-Standard, dh den Tagen von K & R C (vor 1989, dem Jahr, in dem der erste "ANSI C" -Standard veröffentlicht wurde) ).

Denken Sie daran, dass es in K & R C keine Prototypen gab und das Schlüsselwort void noch nicht erfunden wurde. Alles, was Sie tun konnten, war, dem Compiler den Rückgabetyp einer Funktion mitzuteilen. Die leere Parameterliste in K & R C bedeutet "eine nicht spezifizierte, aber feste" Anzahl von Argumenten. Behoben bedeutet, dass Sie die Funktion jedes Mal mit der gleichen Anzahl von Argumenten aufrufen müssen (im Gegensatz zu einer variadischen funktionieren wie printf, wobei die Nummer und der Typ für jeden Anruf unterschiedlich sein können).

Viele Compiler werden dieses Konstrukt diagnostizieren. Insbesondere wird gcc -Wstrict-prototypes Ihnen mitteilen, dass die Funktionsdeklaration kein Prototyp ist, was genau richtig ist, da sie wie ein Prototyp aussieht (vor allem, wenn Sie von C++ vergiftet sind!), ist es aber nicht. Es ist eine alte K & R C-Rückgabetypdeklaration.

Faustregel: Lassen Sie niemals eine leere Parameterlistendeklaration leer, verwenden Sie int func(void), um genau zu sein. Dies macht die K & R-Rückgabetypdeklaration zu einem richtigen C89-Prototyp. Compiler sind glücklich, Entwickler sind glücklich, statische Prüfer sind glücklich. Diejenigen, die von ^ W ^ Wfond in C++ irregeführt werden, können jedoch zusammenzucken, weil sie zusätzliche Zeichen eingeben müssen, wenn sie versuchen, ihre Fremdsprachenkenntnisse zu üben :-)

57
Jens
  • Die leere Parameterliste bedeutet "beliebige Argumente", daher ist die Definition nicht falsch.
  • Der fehlende Typ wird als int angenommen.

Ich würde jeden Build, der dies durchläuft, als fehlend in der konfigurierten Warn-/Fehlerebene betrachten, es hat jedoch keinen Sinn, wenn dies den tatsächlichen Code zulässt.

53
unwind

Es ist K & R Deklaration und Definition von Stilfunktionen. Ab C99-Standard (ISO/IEC 9899: TC3)

Abschnitt 6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen)

Eine Bezeichnerliste deklariert nur die Bezeichner der Parameter der Funktion. Eine leere Liste in einem Funktionsdeklarator, die Teil einer Definition dieser Funktion ist, gibt an, dass die Funktion keine Parameter hat. Die leere Liste in einem Funktionsdeklarator, die nicht Teil einer Definition dieser Funktion ist, gibt an, dass keine Informationen über die Anzahl oder die Typen der Parameter bereitgestellt werden. (Wenn beide Funktionstypen "alten Stils" sind, sind dies Parametertypen nicht verglichen.)

Abschnitt 6.11.6 Funktionsdeklaratoren

Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parameter-Typdeklaratoren im Prototypformat) ist ein veraltetes Merkmal.

Abschnitt 6.11.7 Funktionsdefinitionen

Die Verwendung von Funktionsdefinitionen mit separaten Parameter-ID- und Deklarationslisten (keine Prototyp-Format-Parametertypen und ID-Deklaratoren) ist ein veraltetes Merkmal.

Was der alte Stil bedeutet K & R Stil

Beispiel:

Erklärung: int old_style();

Definition:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}
29
Lei Mou

C nimmt int an, wenn in Funktionsrückgabetyp und Parameterliste kein Typ angegeben ist. Nur für diese Regel sind folgende seltsame Dinge möglich.

Eine Funktionsdefinition sieht so aus.

int func(int param) { /* body */}

Wenn es ein Prototyp ist, schreiben Sie

int func(int param);

Im Prototyp können Sie nur die Art der Parameter angeben. Der Name der Parameter ist nicht obligatorisch. Damit

int func(int);

Auch wenn Sie keinen Parametertyp angeben, aber der Name int als Typ angenommen wird.

int func(param);

Wenn Sie weiter gehen, funktioniert auch das Folgen.

func();

Der Compiler übernimmt int func(), wenn Sie func() schreiben. Setzen Sie jedoch func() nicht in einen Funktionskörper. Das wird ein Funktionsaufruf sein

15
Shiplu Mokaddim

Wie in @Krishnabhadra angegeben, haben alle vorherigen Antworten anderer Benutzer eine korrekte Interpretation, und ich möchte nur einige Punkte genauer analysieren.

Im alten C wie in ANSI-C wird "ntypisierter Formalparameter" die Abmessung Ihres Arbeitsregisters oder Ihrer Befehlstiefenfähigkeit (Schattenregister oder Befehlskumulationszyklus) in einer 8-Bit-MPU annehmen Seien Sie ein int16, in einer 16-Bit-MPU, und so wird es ein int16 und so weiter sein. In diesem Fall können 64-Bit-Architekturen Optionen wie -m32 kompilieren.

Obwohl es auf hoher Ebene einfacher zu sein scheint, mehrere Parameter zu übergeben, wird die Arbeit des Programmierers im Schritt des Datentyps der Steuerungsdimension anspruchsvoller.

In anderen Fällen nutzten die angepassten ANSI-Compiler für einige Mikroprozessorarchitekturen einige dieser alten Funktionen, um die Verwendung des Codes zu optimieren, und zwangen die Position dieser "untypisierten formalen Parameter" dazu, innerhalb oder außerhalb des Arbeitsregisters zu arbeiten fast das gleiche mit der Verwendung von "volatile" und "register".

Es ist jedoch zu beachten, dass die modernsten Compiler keine Unterscheidung zwischen den beiden Arten der Parameterdeklaration treffen.

Beispiele für eine Kompilierung mit gcc unter Linux:

main.c

main2.c

main3.c 
In jedem Fall ist die Angabe des Prototyps vor Ort nicht sinnvoll, da es keinen Aufruf ohne Parameter gibt, der auf diesen Prototyp verweist. Wenn Sie das System mit "untyped formal parameter" für einen externen Aufruf verwenden, generieren Sie einen deklarativen Prototyp-Datentyp.

So was:

int myfunc(int param);
11
RTOSkit

In Bezug auf den Parametertyp gibt es hier bereits richtige Antworten, aber wenn Sie es vom Compiler hören möchten, können Sie versuchen, einige Flags hinzuzufügen (Flags sind sowieso fast immer eine gute Idee).

kompilieren Sie Ihr Programm mit gcc foo.c -Wextra Ich bekomme:

foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]

seltsamerweise fängt -Wextra dies nicht für clang ab (es erkennt -Wmissing-parameter-type aus irgendeinem Grund nicht, vielleicht für die oben erwähnten historischen), aber -pedantic tut dies :

foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.

Und bei einem Prototyp-Problem, wie oben bereits erwähnt, bezieht sich int func() auf beliebige Parameter, es sei denn, Sie definieren es ausdrücklich als int func(void), was dann zu den erwarteten Fehlern führt:

foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here

oder in clang als:

foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
5
none

Wenn die Funktionsdeklaration keine Parameter enthält, d. H. Leer, nimmt sie eine nicht angegebene Anzahl von Argumenten an. Wenn Sie dafür sorgen möchten, dass es keine Argumente gibt, ändern Sie es in:

int func(void);
3
P.P.

Aus diesem Grund rate ich den Leuten normalerweise, ihren Code zu kompilieren mit:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

Diese Flags erzwingen ein paar Dinge:

  • - Missing-Variable-Deklarationen: Es ist unmöglich, eine nicht-statische Funktion zu deklarieren, ohne zuerst einen Prototyp zu erhalten. Dadurch ist es wahrscheinlicher, dass ein Prototyp in einer Header-Datei mit der tatsächlichen Definition übereinstimmt. Alternativ wird das statische Schlüsselwort zu Funktionen hinzugefügt, die nicht öffentlich sichtbar sein müssen.
  • - Strikte Variablendeklarationen: Der Prototyp muss die Argumente korrekt auflisten.
  • -Wold-style-definition: Die Funktionsdefinition selbst muss auch die Argumente korrekt auflisten.

Diese Flags werden standardmäßig auch in vielen Open Source-Projekten verwendet. In FreeBSD sind diese Flags beispielsweise aktiviert, wenn Sie WARNS = 6 in Ihrem Makefile verwenden.

0
Ed Schouten