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

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

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

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

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

Ähnliche Themen