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

Lesezeit: 10 Minuten

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

  1. Das Funktionsprototyp hat im Vergleich zur eigentlichen Funktionsdefinition keine Parameter.

  2. Der Parameter in der Funktionsdefinition hat 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 ein paar Compilern getestet, und es funktioniert gut.

  • Es ist K&R C. Wir haben Code wie diesen in den 1980er Jahren geschrieben, bevor es voll funktionsfähige Prototypen gab.

    – Hughdbrown

    19. Dezember 2012 um 18:27 Uhr

  • gcc warnt mit -Wstrict-prototypes für beide int func() und int main(): xc:3: Warnung: Funktionsdeklaration ist kein Prototyp. Sie sollten deklarieren main() wie main(void) auch.

    – Jens

    19. Dezember 2012 um 21:59 Uhr

  • @Jens Warum hast du die Frage bearbeitet? Du scheinst das Thema verfehlt zu haben…

    – dlras2

    19. Dezember 2012 um 22:02 Uhr

  • Ich habe gerade das implizite int explizit gemacht. Wie verfehlt das den Punkt? Ich glaube, der Punkt ist, warum int func(); ist kompatibel mit int func(arglist) { ... }.

    – Jens

    19. Dezember 2012 um 22:04 Uhr

  • @MatsPetersson Das ist falsch. C99 5.1.2.2.1 widerspricht ausdrücklich Ihrer Behauptung und erklärt, dass die Version ohne Argumente ist int main(void).

    – Jens

    20. Dezember 2012 um 22:47 Uhr

Krishnabhadras Benutzeravatar
Krishnabhadra

Alle anderen Antworten sind richtig, aber nur für Fertigstellung

Eine Funktion wird folgendermaßen deklariert:

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

Rückgabetyp ist der Variablentyp, den die Funktion zurückgibt. Dies kann kein Array-Typ oder ein Funktionstyp sein. Wenn nicht angegeben, wird int angenommen.

Funktionsname ist der Name der Funktion.

Parameterliste ist die Liste der Parameter, die die Funktion durch Kommas getrennt übernimmt. Wenn keine Parameter angegeben sind, nimmt die Funktion keine entgegen und sollte mit leeren Klammern oder mit dem Schlüsselwort void definiert werden. Steht in der Parameterliste vor einer Variablen kein Variablentyp, wird int angenommen. Arrays und Funktionen werden nicht an Funktionen übergeben, sondern automatisch in Zeiger umgewandelt. Wenn die Liste mit Auslassungspunkten (,…) abgeschlossen wird, gibt es keine festgelegte Anzahl von Parametern. Hinweis: Der Header stdarg.h kann verwendet werden, um auf Argumente zuzugreifen, wenn Auslassungspunkte verwendet werden.

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

Das Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parametertypdeklaratoren im Prototypformat) ist eine veraltete Funktion.

  • “Wenn in der Parameterliste vor einer Variablen kein Variablentyp steht, dann wird int angenommen.” Ich sehe das in Ihrem bereitgestellten Link, aber ich kann es in keinem Standard-c89, c99 finden … Können Sie eine andere Quelle angeben?

    – godaygo

    22. Januar 2018 um 8:31 Uhr


  • “Wenn keine Parameter angegeben sind, nimmt die Funktion keine an und sollte mit leeren Klammern definiert werden” klingt im Widerspruch zu Tony The Lions Antwort, aber ich habe gerade erfahren, dass Tony The Lions Antwort auf die harte Tour richtig ist.

    – jakun

    22. Mai 2020 um 16:17 Uhr

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

  • Tatsächlich gibt es keinen einzelnen Typ, der standardmäßig int ist. Tatsächlich sind alle Argumente standardmäßig int (sogar der Rückgabetyp). Anrufen ist völlig in Ordnung func(42,0x42); (wo alle Aufrufe müssen die Form mit zwei Argumenten verwenden).

    – Jens

    19. Dezember 2012 um 14:20 Uhr

  • In C ist es ziemlich üblich, dass nicht spezifizierte Typen standardmäßig verwendet werden int wie zum Beispiel, wenn Sie eine Variable deklarieren: unsigned x; Welchen Typ hat die Variable x? Es stellt sich heraus unsigned int

    – Alex Bitek

    19. Dezember 2012 um 22:32 Uhr


  • Ich würde eher implizites int sagen war in alten Zeiten üblich. Es ist sicherlich nicht mehr und in C99 wurde es aus C entfernt.

    – Jens

    20. Dezember 2012 um 9:09 Uhr

  • Es kann erwähnenswert sein, dass diese Antwort für Funktionsprototypen mit leeren Parameterlisten gilt, aber nicht Funktionsdefinitionen mit leeren Parameterlisten.

    – autistisch

    10. Mai 2013 um 19:19 Uhr

  • @mnemonicflow, das ist kein “nicht spezifizierter Typ, der standardmäßig int ist”; unsigned ist nur ein anderer Name für denselben Typ wie unsigned int.

    – Hobbs

    8. Februar 2015 um 5:52 Uhr

Benutzeravatar von Jens
Jens

int func(); ist eine veraltete Funktionsdeklaration aus der Zeit, als es noch keinen C-Standard gab, also aus den Tagen von K&R C (vor 1989, dem Jahr der Veröffentlichung des ersten “ANSI C”-Standards).

Denken Sie daran, dass es gab keine Prototypen in K&R C und das Stichwort void war noch nicht erfunden. Alles, was Sie tun konnten, war, den Compiler über die zu informieren Rückgabetyp einer Funktion. Die leere Parameterliste in K&R C bedeutet „eine unbestimmte, aber feste“ Anzahl von Argumenten. Fixed bedeutet, dass Sie die Funktion mit aufrufen müssen gleich Anzahl von Argumenten jedes Mal (im Gegensatz zu a variadisch funktionieren wie printfwobei Anzahl und Typ für jeden Anruf unterschiedlich sein können).

Viele Compiler werden dieses Konstrukt diagnostizieren; im Speziellen gcc -Wstrict-prototypes wird Ihnen sagen “Funktionsdeklaration ist kein Prototyp”, was genau richtig ist, weil es sieht aus wie ein Prototyp (besonders wenn Sie von C++ vergiftet sind!), ist es aber nicht. Es ist eine K&R-C-Rückgabetypdeklaration im alten Stil.

Faustregel: Lassen Sie niemals eine leere Parameterlisten-Deklaration leer, verwenden Sie int func(void) um genau zu sein. Dadurch wird 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^W C++ in die Irre geführt werden, könnten jedoch zusammenzucken, weil sie zusätzliche Zeichen eingeben müssen, wenn sie versuchen, ihre Fremdsprachenkenntnisse zu trainieren 🙂

  • Bitte beziehen Sie dies nicht auf C++. es ist gesunder Menschenverstand dass leere Argumentliste bedeutet keine Parameter, und Menschen auf der ganzen Welt sind nicht daran interessiert, sich mit Fehlern auseinanderzusetzen, die K&R-Leute vor etwa 40 Jahren gemacht haben. Aber Komitee-Experten ziehen weiterhin widernatürliche Entscheidungen mit sich – in C99, C11.

    – pfalke

    3. Mai 2014 um 23:17 Uhr

  • @pfalcon Gesunder Menschenverstand ist ziemlich subjektiv. Was für den einen gesunder Menschenverstand ist, ist für den anderen schlichter Wahnsinn. Professionelle C-Programmierer wissen, dass leere Parameterlisten eine nicht spezifizierte, aber feste Anzahl von Argumenten anzeigen. Das zu ändern würde wahrscheinlich viele Implementierungen zerstören. Den Ausschüssen die Schuld dafür zu geben, dass sie stillschweigende Änderungen vermeiden, bellt meiner Meinung nach den falschen Baum an.

    – Jens

    4. Mai 2014 um 10:46 Uhr


Benutzeravatar von unwind
entspannen

  • Die leere Parameterliste bedeutet “beliebige Argumente”, also ist die Definition nicht falsch.
  • Als fehlender Typ wird angenommen int.

Ich würde jedoch davon ausgehen, dass jedem Build, der dies besteht, die konfigurierte Warn- / Fehlerebene fehlt. Es macht keinen Sinn, dies für tatsächlichen Code zuzulassen.

Benutzeravatar von Lei Mou
Lei Mou

Es ist K&R Style-Funktionsdeklaration und -definition. 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, der Teil einer Definition dieser Funktion ist, gibt an, dass die Funktion keine Parameter hat. Die leere Liste in einem Funktionsdeklarator, der nicht Teil einer Definition dieser Funktion ist, gibt an, dass keine Informationen über die Anzahl oder Typen der Parameter bereitgestellt werden. (Wenn beide Funktionstypen “alter Stil” sind, werden Parametertypen nicht verglichen.)

Abschnitt 6.11.6 Funktionsdeklaratoren

Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parametertyp-Deklaratoren im Prototypformat) ist eine veraltetes Merkmal.

Abschnitt 6.11.7 Funktionsdefinitionen

Die Verwendung von Funktionsdefinitionen mit separaten Parameterbezeichnern und Deklarationslisten (keine Parametertyp- und Bezeichnerdeklaratoren im Prototypformat) ist eine 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 */
}

Benutzeravatar von Jens
Jens

C nimmt an int wenn kein Typ für Funktionsrückgabetyp und Parameterliste 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, den Sie schreiben

int func(int param);

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

int func(int);

Auch wenn Sie den Parametertyp nicht angeben, sondern den Namen int wird als Typ angenommen.

int func(param);

Wenn Sie weiter gehen, funktioniert das Folgen auch.

func();

Compiler geht davon aus int func() wenn du schreibst func(). Aber nicht setzen func() innerhalb eines Funktionskörpers. Das wird ein Funktionsaufruf sein

Wie @Krishnabhadra sagte, haben alle vorherigen Antworten von anderen Benutzern eine korrekte Interpretation, und ich möchte nur einige Punkte detaillierter analysieren.

Im Old-C wie im ANSI-C ist das “untypisierter formaler Parameter“, nehmen Sie die Dimension Ihrer Arbeitsregister- oder Befehlstiefenfähigkeit (Schattenregister oder kumulativer Befehlszyklus), in einer 8-Bit-MPU wird es ein int16 sein, in einer 16-Bit-MPU und so wird es ein int16 und so weiter sein, im Fall von 64 Bit Architekturen können sich dafür entscheiden, Optionen zu kompilieren wie: -m32.

Obwohl es eine einfachere Implementierung auf hoher Ebene zu sein scheint, wird die Arbeit des Programmierers in dem Steuerdimensions-Datentypschritt zum Übergeben mehrerer Parameter 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 erzwangen, dass die Position dieser “nicht typisierten formalen Parameter” innerhalb oder außerhalb des Arbeitsregisters funktioniert, heute erhalten Sie fast dasselbe mit der Verwendung von “volatile” und “register”.

Es sollte jedoch beachtet werden, dass die modernsten Compiler keinen Unterschied zwischen den beiden Arten der Parameterdeklaration machen.

Beispiele für eine Kompilierung mit gcc unter Linux:

Haupt c

main2.c

main3.c

In jedem Fall nützt die Angabe des Prototyps lokal nichts, da ohne Parameter kein Aufruf unterlassen wird, auf diesen Prototyp zu verweisen. Wenn Sie das System mit “untypisiertem Formalparameter” verwenden, fahren Sie für einen externen Aufruf mit der Generierung eines deklarativen Prototyp-Datentyps fort.

So was:

int myfunc(int param);

  • Ich habe mir die Mühe gemacht, Bilder so zu optimieren, dass die Größe der Bilder in Bytes so gering wie möglich war. Ich denke, die Bilder können einen schnellen Überblick über den fehlenden Unterschied im generierten Code geben. Ich habe den Code getestet, eine echte Dokumentation dessen erstellt, was er behauptet, und nicht nur über das Problem theoretisiert. aber nicht alle sehen die Welt gleich. Es tut mir schrecklich leid, dass mein Versuch, der Community mehr Informationen zur Verfügung zu stellen, Sie so sehr gestört hat.

    – RTOSkit

    20. Dezember 2012 um 13:13 Uhr

  • Ich bin mir ziemlich sicher, dass ein nicht angegebener Rückgabetyp immer ist intwas im Allgemeinen entweder die Arbeitsregistergröße oder 16 Bit ist, je nachdem, was kleiner ist.

    – Superkatze

    12. Juli 2013 um 18:17 Uhr

  • @supercat +1 Perfekter Abzug, du hast Recht! Genau das bespreche ich oben, ein Compiler, der standardmäßig für eine bestimmte Architektur ausgelegt ist, spiegelt immer die Größe des Arbeitsregisters der betreffenden CPU / MPU wider. In einer eingebetteten Umgebung gibt es also verschiedene Neutypisierungsstrategien Echtzeit-Betriebssystem, in eine Compiler-Schicht (stdint.h) oder in eine Portabilitätsschicht, die das gleiche Betriebssystem in vielen anderen Architekturen portabel macht und durch Angleichung der CPU / MPU-spezifischen Typen (int, long, long long usw.) mit den generischen Systemtypen u8, u16, u32.

    – RTOSkit

    12. Juli 2013 um 23:18 Uhr


  • @supercat Ohne Steuerelementtypen, die von einem Betriebssystem oder einem bestimmten Compiler angeboten werden, müssen Sie ein wenig auf die Entwicklungszeit achten und sicherstellen, dass alle “nicht typisierten Zuweisungen” mit Ihrem Anwendungsdesign übereinstimmen, bevor Sie die Überraschung haben, eine ” 16bit int“ und kein „32bit int“.

    – RTOSkit

    12. Juli 2013 um 23:39 Uhr


  • @RTOSkit: Ihre Antwort legt nahe, dass der Standardtyp auf einem 8-Bit-Prozessor ein Int8 sein wird. Ich erinnere mich an ein paar C-artige Compiler für die Architektur der Marke PICmicro, bei denen dies der Fall war, aber ich glaube nicht, dass irgendetwas, das auch nur im Entferntesten einem C-Standard ähnelt, dies jemals erlaubt hat int Typ, der nicht in der Lage war, alle Werte im Bereich von -32767 bis +32767 zu halten (Hinweis: -32768 ist nicht erforderlich) und auch nicht in der Lage war, alle zu halten char Werte (was bedeutet, dass wenn char ist 16 Bit, entweder muss es vorzeichenbehaftet sein oder int muss größer sein).

    – Superkatze

    13. Juli 2013 um 3:07 Uhr

1427610cookie-checkWarum wird eine Funktion ohne Parameter (im Vergleich zur eigentlichen Funktionsdefinition) kompiliert?

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

Privacy policy