Funktionen mit Rückgabewerten
Medium · 25 min · Kostenlos
Ziel & Lerninhalt
Verstehe den Unterschied zwischen void-Funktionen und solchen mit return-Wert – und wann du welche brauchst.
- Funktionen mit int, float und bool als Rückgabetyp schreiben.
- Rückgabewerte in Variablen speichern und weiterverwenden.
- Sensor-Rohdaten in einer Funktion kalibrieren und als Ergebnis zurückgeben.
Erklärung & Wissen
Basic
Eine void-Funktion tut etwas – z.B. Servo fahren. Eine Funktion mit Rückgabewert berechnet etwas und gibt das Ergebnis zurück. Beispiel: Du willst die Rohzahl von analogRead(A0) (0–1023) in Volt (0–5 V) umrechnen. Statt das überall im Code zu schreiben, schreibst du eine Funktion `float rohZuVolt(int roh)` und rufst sie auf: `float v = rohZuVolt(analogRead(A0));`. Sauber, wiederverwendbar, an einer Stelle zu korrigieren.
Medium
Der Rückgabetyp steht vor dem Funktionsnamen. `int` gibt eine ganze Zahl zurück, `float` eine Dezimalzahl, `bool` wahr oder falsch. Das `return`-Schlüsselwort sendet den Wert zurück und beendet die Funktion sofort. Wichtig: Der Typ der zurückgegebenen Variable muss mit dem deklarierten Rückgabetyp übereinstimmen – der Compiler prüft das streng. Du kannst auch früh returnen: `if (roh < 0) return 0.0;` als Schutz vor Fehleingaben.
Expert
Rückgabewerte werden im CPU-Register übergeben (bei AVR: R24/R25 für 16-bit-Werte, R22–R25 für 32-bit). Der Compiler entscheidet, ob er die Funktion inlined (Code direkt einfügt, kein Funktionsaufruf-Overhead) oder als echter Unterprogramm-Sprung. Inlining lohnt sich bei kurzen Funktionen (< 5 Zeilen). Für Sensorfunktionen: Lieber `int` statt `float` verwenden, wenn die Genauigkeit reicht – int-Arithmetik ist auf AVR 10× schneller als Float-Arithmetik.
Fun Fact
In frühen FORTRAN-Versionen (1957) konnten Funktionen nur einen einzigen Rückgabewert haben. Wollte man mehrere Werte zurückgeben, musste man globale Variablen oder COMMON-Blöcke missbrauchen – ein Design, das noch Jahrzehnte später Bugs verursachte.
- Rückgabetyp
- Der Datentyp des Wertes, den die Funktion nach ihrer Arbeit zurückliefert. Mögliche Typen in Arduino/C++: `void` (nichts), `int` (ganzzahlig, ±32767), `long` (groß), `float` (Dezimal, ±3.4e38, 6–7 Stellen Genauigkeit), `bool` (true/false), `char` (ein Zeichen). Falsche Typ-Zuordnung erzeugt stillen Datenverlust: float → int schneidet Nachkommastellen ab.
- Laufzeitfehler
- Fehler, die der Compiler nicht findet, aber beim Ausführen auftreten. Beispiele: Division durch null (int/0 = undefiniert auf AVR, kein Crash, aber falsche Werte), Array-Index out of bounds (Speicher wird überschrieben), Integer-Überlauf (255 + 1 ergibt bei uint8_t den Wert 0). Arduino-Controller stürzen nicht ab, sondern liefern still falsche Werte – besonders tückisch.
- Stack Overflow
- Wenn der Aufruf-Stapel zu tief wird und Speicher überläuft. Auf ATmega328: nur 2 KB SRAM für Stack + lokale Variablen. Tiefe Rekursion (Funktion ruft sich selbst auf) erschöpft diesen rasch. Symptom: Controller verhält sich zufällig seltsam oder hängt sich auf. Debugging: Mit `Serial.println(SP)` (Stack Pointer) lässt sich der verbleibende Stack-Platz schätzen.
- Frühes Return
- return mitten in einer Funktion als Schutzklausel. Guard Clause Pattern: `if (wert < 0) return 0;` am Anfang der Funktion filtert ungültige Eingaben sofort aus. Das vermeidet tiefe Verschachtelung von if/else und macht den Normalfall-Code flach und lesbar. Standard in professionellem C++-Code.
Praktischer Versuch: Sensor-Kalibrierung als Rückgabe-Funktion
Schreibe eine Funktion, die einen ADC-Rohwert liest, in Volt umrechnet und zurückgibt. Dann eine zweite, die Volt in Servo-Winkel mappt.
- Stück Alufolie oder blanker Draht an A0 (Finger-Berührungs-Sensor)
void vs. return verstehen
Schreibe eine Funktion `void testFunktion() { return 42; }`. Kompiliere und lies die Fehlermeldung. Ändere void zu int und beobachte, dass der Fehler verschwindet.
Check: Compiler-Fehler bei void-Funktion mit return-Wert ist bekannt.
Tipp: Absichtlich falsch schreiben, Fehlermeldung lesen – eine der besten Lernmethoden.
rohZuVolt() schreiben
Schreibe `float rohZuVolt(int roh) { return roh * 5.0 / 1023.0; }`. Rufe auf: `float v = rohZuVolt(analogRead(A0));` und schreibe v in den Monitor.
Check: Serieller Monitor zeigt Volt-Werte zwischen 0.00 und 5.00.
Tipp: Rückgabetyp float, Berechnung: roh * 5.0 / 1023.0 (5.0 erzwingt float-Division).
voltZuWinkel() schreiben
Schreibe `int voltZuWinkel(float volt) { return constrain((int)(volt / 5.0 * 180.0), 0, 180); }`. Teste mit verschiedenen Finger-Berührungen am A0-Pin.
Check: Bei 0 V gibt Funktion 0° zurück, bei 5 V gibt sie 180° zurück.
Tipp: map()-Funktion von Arduino kann auch float-Werte verarbeiten mit selbst implementiertem map.
Fehlerfall absichern
Füge in rohZuVolt() eine Schutzklausel ein: `if (roh < 0) return 0.0; if (roh > 1023) return 5.0;`. Teste die Randfälle manuell.
Check: Auch bei roh=0 oder roh=1023 gibt die Funktion sinnvolle Werte zurück.
Tipp: constrain() ist dein Freund: verhindert, dass out-of-range-Werte den Servo beschädigen.
Beide Funktionen verketten
In loop(): `servo.write(voltZuWinkel(rohZuVolt(analogRead(A0))));`. Beobachte: Finger auf A0 → Volt-Wert → Winkel → Servo-Bewegung.
Check: Servo folgt dem Berührungs-Input am A0-Pin über die Funktionskette.
Tipp: Funktionen können verkettet werden: voltZuWinkel(rohZuVolt(analogRead(A0)))
Servo an D9. A0 offen (Finger als Testsignal).
// ProtoPeaks - Rückgabewert-Funktionen
#include <Servo.h>
Servo servo;
float rohZuVolt(int roh) {
if (roh < 0) return 0.0;
if (roh > 1023) return 5.0;
return roh * 5.0 / 1023.0;
}
int voltZuWinkel(float volt) {
return constrain((int)(volt / 5.0 * 180.0), 0, 180);
}
void setup() {
Serial.begin(9600);
servo.attach(9);
}
void loop() {
int roh = analogRead(A0);
float volt = rohZuVolt(roh);
int winkel = voltZuWinkel(volt);
Serial.print(roh); Serial.print("\t");
Serial.print(volt, 2); Serial.print("V\t");
Serial.print(winkel); Serial.println("°");
servo.write(winkel);
delay(50);
}- Servo springt unkontrolliert bei offenem A0-Pin. — Floating Pin rauscht. Finger fest auf A0 halten oder Pull-Down-Widerstand (10 kΩ) von A0 nach GND.
- Volt-Wert immer 5.0, egal was man macht. — Roh-Wert ist 1023 → Schutzklausel greift. Widerstand einbauen, damit Wert sicher im Bereich 0–1023 liegt.
Kurzzusammenfassung
Rückgabewert-Funktionen trennen Berechnung von Ausgabe. rohZuVolt() und voltZuWinkel() sind typische Konvertierungs-Helfer im ProtoPeaks-Code. Frühes Return schützt vor Randfällen, constrain() hält Werte in erlaubten Grenzen.