
Der ANSI C- Standard definiert das Konzept eines Funktionsprototyps , der eine Teilmenge einer Funktionsdeklaration ist, die die Arten von Eingabeparametern angibt. Prototypen wurden eingeführt, um die Nachteile zu beseitigen, die übliche Funktionsdeklarationen aufweisen.
Die Angabe einer Liste von Parametertypen in Klammern eines Funktionsprototyps ist daher obligatorisch. Andernfalls wird ein solcher Ausdruck vom Compiler als veraltete Funktionsdeklaration erkannt, was zu mehrdeutigen Situationen führen kann, die in diesem Artikel beschrieben werden.
Veraltete Prototypen
Eine Funktionsdeklaration führt den Rückgabetyp der Funktion und ihren Bezeichner in den angegebenen Bereich ein . Beachten Sie, dass nicht alle Funktionsdeklarationen als Prototypen betrachtet werden können, sondern nur diejenigen, die eine Liste von Eingabeparametertypen enthalten.
Daher ist der erste Ausdruck des folgenden Codes eine Deklaration, jedoch kein Funktionsprototyp. Der folgende Ausdruck kann zu Recht als Prototyp betrachtet werden, da er die Typen seiner Parameter angibt:
void foo(); void bar(int count, const char *word);
Veraltete Definitionen
Gehen wir direkt zu 1972 (dem Jahr, in dem die C-Sprache veröffentlicht wurde) und erinnern uns, wie die damaligen Programmierer ihre Funktionen definiert haben. Ich möchte Sie daran erinnern, dass die Definition einer Funktion ihre Signatur mit dem entsprechenden ausführbaren Block (body) verbindet. Dieser Code demonstriert die Definition der add
Funktion im K & R-Stil:
void add(right, left, result) int right; int left; int *result; { *result = right + left; }
Wie Sie vielleicht bemerkt haben, kennzeichnen Klammern in diesem Eintrag die Funktion, enthalten jedoch keine Arten von Eingabeparametern. Die im vorherigen Abschnitt beschriebenen „klassischen“ Funktionsdeklarationen haben dieselbe Eigenschaft.
Mehrdeutige Situationen
Es ist möglich, dass schwierige, mehrdeutige Situationen auftreten können, wenn die durch den ANSI C-Standard eingeführte neue Syntax von Prototypen und Funktionsdefinitionen nicht eingehalten wird. Betrachten Sie ein Beispiel:
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <limits.h> /* "print_number" */ void print_number(); int main(void) { /* */ print_number((double)13.359); print_number((double)9238.46436); print_number((double)18437); /* */ print_number(UINT64_MAX); print_number("First", "Second", "Third"); print_number(NULL, "Breakfast", &print_number); } void print_number(double number) { printf(" : [%f]\n", number); }
Wir werden dieses Programm analysieren. Die korrekte Funktion print_number
ohne Angabe einer Liste von Parametertypen deklariert, sodass Sie diese Funktion mit beliebigen Argumenten aufrufen können. Das Programm wurde fehlerfrei kompiliert und das folgende Ergebnis gedruckt:
$ gcc illegal.c -o illegal -Wall $ ./illegal : [13.359000] : [9238.464360] : [18437.000000] : [0.000000] : [0.000000] : [0.000000]
Beachten Sie auch, dass der -Wall
Compiler gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
selbst mit dem Flag gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
keine Warnungen generiert hat (dies wäre jedoch sehr wünschenswert).
Das Reparieren dieses Programms ist nicht schwierig. print_number
einfach die double number
in Klammern der Deklaration der Funktion print_number
in die siebte Zeile ein. print_number
jeder Compiler, der dem Standard folgt, Fehler in der Funktion main()
:
$ gcc -Wall illegal.c -o illegal illegal.c: In function 'main': illegal.c:17:22: error: incompatible type for argument 1 of 'print_number' print_number("First", "Second", "Third"); ^~~~~~~ illegal.c:7:6: note: expected 'double' but argument is of type 'char *' void print_number(double number); ^~~~~~~~~~~~ illegal.c:17:9: error: too many arguments to function 'print_number' print_number("First", "Second", "Third"); ^~~~~~~~~~~~ illegal.c:7:6: note: declared here void print_number(double number); ^~~~~~~~~~~~ illegal.c:18:22: error: incompatible type for argument 1 of 'print_number' print_number(NULL, "Breakfast", &print_number); ^~~~ illegal.c:7:6: note: expected 'double' but argument is of type 'void *' void print_number(double number); ^~~~~~~~~~~~ illegal.c:18:9: error: too many arguments to function 'print_number' print_number(NULL, "Breakfast", &print_number); ^~~~~~~~~~~~ illegal.c:7:6: note: declared here void print_number(double number); ^~~~~~~~~~~~
Funktionen ohne Parameter
Ich stelle auch fest, dass die Angabe des Schlüsselworts void
in Klammern von Prototypen und Definitionen von Funktionen, die keine Parameter annehmen, äußerst wünschenswert (aber nicht erforderlich) ist. Wenn diese Regel nicht eingehalten wird, kann der Compiler die Entsprechung der Typen der übergebenen Argumente nicht überprüfen, wenn die Funktion mit gültigen Typen aus der Definition aufgerufen wird.
#include <stdio.h> /* "do_something" */ void do_something(); int main(void) { /* "do_something" */ do_something(NULL, "Papa Johns", 2842, 1484.3355); } void do_something() { puts("I am doing something interesting right now!"); }
Korrigieren Sie den obigen Code, indem Sie das Schlüsselwort void
in die Definition und Deklaration der Funktion do_something()
. Andernfalls wird dieses Programm fehlerfrei kompiliert. In diesem Beispiel wird die Funktion main()
auch mit dem void
Token in den Parametern definiert, obwohl dies nicht erforderlich ist.
Fazit
Das Schreiben dieses Artikels wurde von Stephen Prats Buch "C-Programmiersprache. Vorlesungen und Übungen. Sechste Ausgabe" und speziell dem Abschnitt "Funktionen mit Argumenten" des fünften Kapitels inspiriert.