Forum: Compiler & IDEs wozu Prototypen deklarieren?


von Melder (Gast)


Lesenswert?

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"...

von johnny.m (Gast)


Lesenswert?

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);

von Karl heinz B. (kbucheg)


Lesenswert?

> 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.

von A.K. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.