Servus warum braucht AVRStudio eigentlich immer diese Prototypen-Definition uint8_t function( uint8_t data); am Anfang des Quellcodes? Jedesmal wenn ich eine Funktion schreibe, die irgendwo innerhalb von main() aufrufe, kommt die Warnung einer "implizit deklarierten Funktion"...
AVRStudio braucht keine Prototypen. Die braucht der Compiler, und zwar dann, wenn die Funktion vor ihrer Definition bereits aufgerufen wird. Der Compiler muss vor dem ersten Aufruf wissen, dass eine Funktion des Namens existiert, auch wenn sie erst später oder in einer anderen Source-Datei definiert wird. Außerdem muss der Compiler wissen, welche Datentypen übergeben werden. Die Namen der Übergabe-Parameter (in diesem Falle "data") müssen hingegen erst bei der Definition bekannt sein. Es genügt bei einem Prototypen daher auch, zu schreiben (für das obige Beispiel): uint8_t function(uint8_t);
> Es genügt
Rein rechtlich: ja.
Gute Idee ist es aber keine.
Zum Thema Prototypen:
Das Ur-C (K&R C) brauchte die noch nicht. Man konnte, musste
aber nicht. Es trat dann die 'implizit-int' Rgel in Kraft.
D.h. der Return-Typ der Funktion wurde implizit ls int
angenommen. Die Datentypen der Argumente konnte sich der
Compiler ja aus em Funktionsaufruf ableiten.
Das brachte dann so lustige, und für den unbedarften Programmierer
ausserst schwer zu findende, Fehler, wie:
1 | int main() |
2 | {
|
3 | double x; |
4 | |
5 | x = sin(0); |
6 | }
|
und jetzt such mal schön :-) * Da kein Prototyp für sin vorhanden ist, geht der Compiler davon aus, dass die Funktion einen int returned. Tut sie aber nicht. sin liefert einen double. D.h. die Funktion legt einen double für den Rückgabemechanismus bereit, der Aufrufer hingegen behandelt die Bytes als ob sie ein int wären. * Da kein Prototyp für sin vorhanden ist, muss der Compiler die Argumenttypen aus dem Aufruf ableiten. Im obigen Fall kommt er zum Schluss, dass sin wohl einen int nehmen wird, da ja 0 ein int darstellt. D.h. der Compiler baut den Aufruf so, dass als Argument die Bytes für einen int übergeben werden. Nur: sin nimmt einen double! d.h. die Implementierung für sin nimmt die Bytes die es kriegen kann und interpretiert sie als double. * mal davon abgesehen, dass sizeof(int) meistens nicht gleich sizeof(double) ist, haben int und double aber auch völlig andere Repräsentierungen als Bytes, so dass hier ein heilloses Durcheinander ensteht. Abhilfe schafft nur ein Prototyp:
1 | double sin(double); |
2 | |
3 | int main() |
4 | {
|
5 | double x; |
6 | |
7 | x = sin(0); |
8 | }
|
Jetzt weiss der Compiler, dass es eine Funktion namens 'sin' gibt (könnte ja auch ein Tippfehler sein), dass die Funktion einen double erwartet und einen double zurückliefert.
Hat auch was mit Programmierphilosophie zu tun, d.h. dem individuellem Geschmack. (1) Top Down: main() ist oben, daraus aufgerufene Funktionen darunter, Kleinkram ganz unten. In diesem Fall kommt man kaum darum herum, die am Anfang vom Source die Prototypes aufzulisten. (2) Bottom Up: Genau andersrum. main() ist hinten, der Rest davor. Prototypes kann man sich meist sparen, es sein denn man hat mit Rekursion zu tun. (3) Man programmiert irgendwie, und sei es wild durcheinander, will aber schon der Übersichtlichkeit halb immer erkennen können, in welchem Sourcefile welche lokalen Funktionen definiert sind. Und listet sie daher fein säuberlich oben auf.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.