Sind Prototypen für alle Funktionen in C89, C90 oder C99 erforderlich?

Lesezeit: 10 Minuten

Müssen alle Funktionen in C (außer main) einen Prototyp haben, um wirklich standardkonform zu sein, auch wenn sie erst nach ihrer Definition in derselben Übersetzungseinheit verwendet werden?

  • Die Frage Muss Funktionsprototyp in C deklarieren? wurde einmal als Duplikat von diesem vorgeschlagen. Es müsste einen triftigen Grund geben, eine ältere Frage als Duplikat einer neueren zu schließen und nicht umgekehrt.

    – Jonathan Leffler

    24. August 2015 um 14:08 Uhr

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Es hängt davon ab, was Sie unter „wirklich standardkonform“ verstehen. Die kurze Antwort lautet jedoch: „Es ist eine gute Idee, sicherzustellen, dass alle Funktionen einen Prototyp im Umfang haben, bevor sie verwendet werden“.

Eine qualifiziertere Antwort stellt fest, dass, wenn die Funktion variable Argumente akzeptiert (insbesondere die printf() Funktionsfamilie), dann muss ein Prototyp im Geltungsbereich liegen, um streng standardkonform zu sein. Dies gilt für C89 (von ANSI) und C90 (von ISO; dasselbe wie C89 mit Ausnahme der Abschnittsnummerierung). Anders als ‘varargs’-Funktionen sind jedoch Funktionen, die eine zurückgeben int müssen nicht deklariert werden, und Funktionen, die etwas anderes als an zurückgeben int benötigen eine Deklaration, die den Rückgabetyp anzeigt, benötigen jedoch keinen Prototyp für die Argumentliste.

Beachten Sie jedoch, dass, wenn die Funktion Argumente akzeptiert, die in Abwesenheit von Prototypen „normalen Heraufstufungen“ unterliegen (z. B. eine Funktion, die a char oder short – beide werden umgewandelt in int; ernster vielleicht eine Funktion, die eine übernimmt float anstelle einer double), dann wird ein Prototyp benötigt. Der Standard war in dieser Hinsicht lax, um zu ermöglichen, dass alter C-Code unter standardkonformen Compilern kompiliert werden kann; Älterer Code wurde nicht geschrieben, um sicherzustellen, dass Funktionen vor der Verwendung deklariert wurden – und älterer Code verwendete definitionsgemäß keine Prototypen, da sie in C nicht verfügbar wurden, bis es einen Standard gab.

C99 verbietet ‘implicit int’ … das bedeutet beide Sonderfälle wie ‘static a;‘ (ein int standardmäßig) und auch implizite Funktionsdeklarationen. Diese werden (zusammen mit etwa 50 anderen wichtigen Änderungen) im Vorwort zu ISO/IEC 9899:1999 erwähnt, das diesen Standard mit den vorherigen Versionen vergleicht:

  • implizit entfernen int

  • Implizite Funktionsdeklaration entfernen

In ISO/IEC 9899:1990, §6.3.2.2 Funktionsaufrufe angegeben:

Besteht der Ausdruck, der bei einem Funktionsaufruf der eingeklammerten Argumentliste vorangeht, ausschließlich aus einem Bezeichner und ist für diesen Bezeichner keine Deklaration sichtbar, so wird der Bezeichner implizit genauso deklariert, als ob im innersten Block, der den Funktionsaufruf enthält, die Deklaration:

extern int identifier();

erschien.38

38 Das heißt, ein Bezeichner mit Blockbereich, der für eine externe Verknüpfung mit Typfunktion ohne Parameterinformationen deklariert ist und eine zurückgibt int. Wenn es tatsächlich nicht als Typ „Funktionsrückkehr int“, ist das Verhalten undefiniert.

Dieser Absatz fehlt in der Norm von 1999. Ich habe (noch) nicht die Änderung der Wortwahl verfolgt, die dies zulässt static a; in C90 und verbietet es (erfordert static int a;) in C99.

Beachten Sie, dass eine statische Funktion vor ihrer Verwendung definiert werden kann und ihr keine Deklaration vorangestellt werden muss. GCC kann zu Twitter überredet werden, wenn eine nicht-statische Funktion ohne vorangestellte Deklaration definiert wird (-Wmissing-prototypes).

  • Punkte für den humorvollen Gebrauch von “witter” (um ausführlich über ein triviales Thema zu sprechen). Ich würde für den allgemeinen Missbrauch von “Wort” (übertriebene Wortigkeit) zur Bedeutung von “Sprache” abziehen, aber nach Berücksichtigung des Textes der C-Standards , entschied ich mich, es als subtileren und sehr zielgerichteten Humor aufzufassen.

    – Jeff Learmann

    3. August 2018 um 18:47 Uhr

EIN Prototyp ist eine Funktionsdeklaration, die die Typen der Funktionsparameter angibt.

Pre-ANSI C (die Sprache, die 1978 in der ersten Ausgabe von Kernighan & Ritchies “The C Programming Language” beschrieben wurde) hatte keine Prototypen; es war nicht möglich, dass eine Funktionsdeklaration die Anzahl oder Typen der Parameter beschreibt. Es war Sache des Aufrufers, die richtige Anzahl und Art der Argumente zu übergeben.

ANSI C führte “Prototypen” ein, Deklarationen, die die Typen der Parameter spezifizieren (eine aus dem frühen C++ entlehnte Funktion).

Ab C89/C90 (die ANSI- und ISO-Standards beschreiben dieselbe Sprache) ist es legal, eine Funktion ohne sichtbare Deklaration aufzurufen; eine implizite Deklaration ist vorgesehen. Wenn die implizite Deklaration nicht mit der tatsächlichen Definition kompatibel ist (z. B. Aufrufen von sqrt("foo"), dann ist das Verhalten undefiniert. Weder diese implizite Deklaration noch eine Nicht-Prototyp-Deklaration können mit einer variadischen Funktion kompatibel sein, sodass jeder Aufruf einer variadischen Funktion (wie z printf oder scanf) muss einen sichtbaren Prototyp haben.

C99 ließ implizite Deklarationen fallen. Jeder Aufruf einer Funktion ohne sichtbare Deklaration stellt eine Einschränkungsverletzung dar und erfordert eine Compilerdiagnose. Aber diese Deklaration muss immer noch kein Prototyp sein; es kann sich um eine Deklaration im alten Stil handeln, die keine Parametertypen angibt.

C11 hat in diesem Bereich keine wesentlichen Änderungen vorgenommen.

So sind selbst nach dem ISO-C-Standard von 2011 Funktionsdeklarationen und -definitionen im alten Stil (die seit 1989 „veraltet“ sind) in konformem Code noch zulässig.

Für alle Versionen von C seit 1989 gibt es aus Stilgründen kaum einen Grund, Prototypen nicht für alle Funktionen zu verwenden. Deklarationen und Definitionen im alten Stil werden nur beibehalten, um zu verhindern, dass alter Code beschädigt wird.

  • Mit Deklarationen alten Stils kann eine bestimmte Semantik erreicht werden, die mit Prototypen nicht erreichbar wäre. Beispielsweise kann eine Funktion, die manchmal ihre Parameter verwendet, rechtmäßig ohne Argumente aufgerufen werden, wenn sie dies weiß [perhaps on the basis of a global or static variable] dass es keinen seiner Parameter untersuchen sollte. Wenn eine API solche Fähigkeiten erfordert, ist es möglicherweise nicht möglich, sie mit neuartigen Prototypen zu codieren.

    – Superkatze

    25. Dezember 2016 um 21:21 Uhr

  • @supercat: Falsch. Wenn es sich um eine nicht-variadische Funktion handelt Definition beispielsweise 2 Parameter deklariert, dann hat ein Aufruf, der nicht genau 2 Argumente des/der geeigneten Typ(s) übergibt, ein undefiniertes Verhalten. Die Verwendung einer Nicht-Prototyp-Deklaration hindert den Compiler lediglich daran, den Fehler zu diagnostizieren.

    – Keith Thompson

    25. Dezember 2016 um 21:46 Uhr

  • … in den Tagen vor dem Standard waren alle zukünftigen Implementierungen für diese Plattform, die diesen bereits vorhandenen Code unterstützen mussten, gezwungen, variadische Aufrufe zu unterstützen, unabhängig davon, ob der Standard dies erforderte oder nicht.

    – Superkatze

    25. Dezember 2016 um 22:52 Uhr

  • Der Standard ganz explizit unterstützt nicht wovon redest du. N1570 6.5.2.2 Absatz 6: “Wenn die Anzahl der Argumente nicht gleich der Anzahl der Parameter ist, ist das Verhalten undefiniert.” Der bereits vorhandene Code, von dem Sie sprechen, ist genau der Grund <stdarg.h> und explizite variadische Funktionen wurden eingeführt. Ein Beispiel dafür, wovon Sie sprechen, ist die POSIX open() Funktion, die traditionell 2 oder 3 Argumente akzeptiert; POSIX spezifiziert es als variadische Funktion. Die Frage bezieht sich auf C89/C90 und C99, nicht auf Pre-ANSI C.

    – Keith Thompson

    25. Dezember 2016 um 22:55 Uhr

  • Wenn Sie solche schrecklich nicht übertragbaren Praktiken befürworten wollen, machen Sie zumindest deutlich, dass sie nicht übertragbar sind und dass Sie Annahmen auf der Grundlage eines 42 Jahre alten Dokuments treffen, das mehrfach ersetzt wurde mal. Das Übergeben der falschen Anzahl von Argumenten an eine Funktion ist nicht portierbar. und ist keine gängige Praxis.

    – Keith Thompson

    25. Dezember 2016 um 23:05 Uhr

Nein, Funktionen brauchen nicht immer einen Prototypen. Die einzige Voraussetzung ist, dass eine Funktion “deklariert” wird, bevor Sie sie verwenden. Es gibt zwei Möglichkeiten, eine Funktion zu deklarieren: einen Prototypen zu schreiben oder die Funktion selbst zu schreiben (als “Definition” bezeichnet). Eine Definition ist immer eine Deklaration, aber nicht alle Deklarationen sind Definitionen.

  • Bei C99 hast du Recht. In C89/C90 musste eine Funktion nicht vorab deklariert werden; Es würde implizit als Funktion deklariert, die eine undefinierte Liste von Argumenten nimmt und int zurückgibt, indem es einfach als Funktion verwendet wird.

    – Jonathan Leffler

    13. Januar 2009 um 14:31 Uhr

  • Diese Unterscheidung zwischen C99- und Pre-C99-Standards kann erheblich sein, wie diese comp.lang.c-FAQ-Frage zeigt: c-faq.com/malloc/mallocnocast.html

    – nagul

    5. August 2009 um 10:18 Uhr

  • Gute Antwort, obwohl Sie vielleicht bemerken, dass einige Compiler, die auf einen Aufruf einer nicht deklarierten Funktion gestoßen sind, davon ausgehen würden, dass es sich um eine handelt int Funktion, deren Argumente genau mit dem übereinstimmten, was im Aufruf übergeben wurde, unter der Annahme von Standard-Promotions. Solche Compiler würden im Allgemeinen einen Fehler ausgeben, wenn in derselben Kompilierungseinheit eine Deklaration gefunden würde, die der gefolgerten widerspricht. Wenn keine Deklaration gefunden wurde und die Argumenttypen nicht richtig erraten wurden (im Vergleich zu einer separat kompilierten Funktionsdefinition), kann das Problem zur Verbindungszeit erkannt werden oder auch nicht.

    – Superkatze

    11. Juni 2012 um 15:30 Uhr

  • Eine Deklaration “int foo();” ist kein Prototyp, würde aber ausreichen, um Code zu erlauben, “foo” mit einer beliebigen Anzahl von Parametern aufzurufen, vorausgesetzt, dass “foo” irgendwo im “alten” Stil definiert ist und vorausgesetzt, dass es nie versucht, mehr Argumente als zu verwenden werden daran weitergegeben.

    – Superkatze

    25. Dezember 2016 um 21:22 Uhr

  • @supercat: Tut mir leid, dass ich nicht früher auf diesen Kommentar geantwortet habe. Das ist falsch. Wenn foo mit Parametern aufgerufen wird, die nicht mit seiner Definition übereinstimmen, ist das Verhalten undefiniert. Zum Beispiel, wenn foo ist mit 2 definiert int Parameter, Aufruf mit 3 foo Parameter hat undefiniertes Verhalten. Was auch immer Sie mit diesem nicht tragbaren Hack zu tun versuchen, es gibt einen besseren und tragbareren Weg, es zu tun.

    – Keith Thompson

    11. Juli 2019 um 21:46 Uhr

Ein netter Tipp beim Schreiben neuer Funktionen ist, sie verkehrt herum mit main unten zu schreiben, damit Sie nicht auch den Prototyp reparieren müssen, wenn Sie Ihre Meinung über die Argumente oder den Rückgabetyp der Funktion ändern. Das ständige Reparieren von Prototypen und der Umgang mit all den Warnungen des Compilers, wenn sie veraltet sind, wird wirklich mühsam.

Sobald Ihre Funktionen reibungslos zusammenarbeiten, verschieben Sie den Code in ein gut benanntes Modul und legen Sie die Prototypen in einer .h-Datei mit demselben Namen ab. Es spart viel Zeit. Die größte Produktivitätshilfe, die ich seit 5 Jahren gefunden habe.

Ja, jede Funktion muss einen Prototyp haben, aber dieser Prototyp kann entweder in einer separaten Deklaration oder als Teil der Funktionsdefinition erscheinen. Funktionsdefinitionen, die in C89 und höher geschrieben wurden, haben natürlich Prototypen, aber wenn Sie Dinge im klassischen K&R-Stil schreiben, also:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

dann hat die Funktionsdefinition keinen Prototyp. Wenn Sie den Stil ANSI C (C89) schreiben, also:

main (int argc, char **argv) { ... }

dann hat die Funktionsdefinition einen Prototyp.

  • K&R-Funktionsdefinitionen sind in C89 immer noch legal (obwohl nicht empfohlen), daher ist die Aussage “Jede Funktion muss einen Prototyp haben” nicht wahr.

    – MM

    28. August 2015 um 0:30 Uhr

  • Diese Antwort widerspricht sich selbst, ist aber nützlich, um den K&R C-Stil zum Definieren von Funktionsargumenten in der Funktionsdefinition zu präsentieren. Man hofft, solchen Code nie mehr zu sehen, aber manchmal müssen wir Code-Archäologie betreiben!

    – Jeff Learmann

    3. August 2018 um 18:56 Uhr

  • @JeffLearman: Es kann nützlich sein, aber es ist sachlich falsch.

    – Keith Thompson

    3. August 2018 um 19:19 Uhr

  • @KeithThompson Stimmt, und das beeinträchtigt seine Nützlichkeit erheblich. Sonst hätte ich dafür gestimmt.

    – Jeff Learmann

    3. August 2018 um 19:45 Uhr

Nach meinem besten Wissen (in ANSI C89/ISO C90), nein. Bei C99 bin ich mir nicht sicher; Ich würde jedoch dasselbe erwarten.

Persönliche Anmerkung: Funktionsprototypen schreibe ich nur, wenn…

  1. Ich muss (wenn A() B() aufruft und B() ruft A() auf), oder
  2. Ich exportiere die Funktion; ansonsten fühlt es sich überflüssig an.

  • K&R-Funktionsdefinitionen sind in C89 immer noch legal (obwohl nicht empfohlen), daher ist die Aussage “Jede Funktion muss einen Prototyp haben” nicht wahr.

    – MM

    28. August 2015 um 0:30 Uhr

  • Diese Antwort widerspricht sich selbst, ist aber nützlich, um den K&R C-Stil zum Definieren von Funktionsargumenten in der Funktionsdefinition zu präsentieren. Man hofft, solchen Code nie mehr zu sehen, aber manchmal müssen wir Code-Archäologie betreiben!

    – Jeff Learmann

    3. August 2018 um 18:56 Uhr

  • @JeffLearman: Es kann nützlich sein, aber es ist sachlich falsch.

    – Keith Thompson

    3. August 2018 um 19:19 Uhr

  • @KeithThompson Stimmt, und das beeinträchtigt seine Nützlichkeit erheblich. Sonst hätte ich dafür gestimmt.

    – Jeff Learmann

    3. August 2018 um 19:45 Uhr

1410340cookie-checkSind Prototypen für alle Funktionen in C89, C90 oder C99 erforderlich?

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

Privacy policy