Grundlagen der Programmierung
Prof. Dr. Alexandra Mikityuk
HTW Berlin
void reicht(void) bei Parametern bedeutetStellt euch ein Programm vor, das dreimal einen Notendurchschnitt rechnet — für drei verschiedene Klassen.
// Klasse A — 8 Zeilen
summe = 0;
for (i=0; i<n; i++) summe += a[i];
schnitt = summe / n;
...
// Klasse B — 8 Zeilen kopiert
// Klasse C — 8 Zeilen kopiert
3× kopiert → 3× Bug-Risiko. Beim Korrigieren: alle 3 anpassen.
// Einmal definiert:
double durchschnitt(int a[], int n) {
// ... Logik ...
}
// Dreimal aufgerufen:
schnittA = durchschnitt(klasseA, 30);
schnittB = durchschnitt(klasseB, 28);
Eine Mini-Maschine mit klarer Aufgabe — Input rein, etwas passiert (oder ein Wert kommt raus).
f(x) = x²
Eingabe x = 3 → Ausgabe 9.
int quadrat(int x) {
return x * x;
}
quadrat(3) → 9.
main und printf sind auch Funktionen — die habt ihr schon die ganze Zeit benutzt. Heute lernt ihr, eure eigenen zu bauen.
int addiere (int a, int b) {
↑ ↑ ↑
① Typ ② Name ③ Parameter
return a + b;
} ← ④ Body
Was die Funktion zurückgibt: int, double, char — oder void für „nichts".
Wie die Funktion aufgerufen wird. Verb ist gut: berechne_, drucke_.
Eingaben: Liste von Typ + Name, durch Kommas getrennt.
// Definition
void begruessung(void) {
printf("Hallo Welt!\n");
printf("Willkommen!\n");
}
// Aufruf
int main(void) {
begruessung(); // Klammern, kein Argument
begruessung(); // nochmal — gleicher Code, kein Copy-Paste
return 0;
}
void in dieser Zeile?
void = „gibt nichts zurück"(void) = „erwartet nichts"(void) und nicht einfach ()?In C sind int main() und int main(void) nicht das Gleiche!
int main()Der Compiler liest: „die Parameter-Liste ist nicht spezifiziert".
Er weiß nicht, ob die Funktion 0 oder 10 Parameter hat — und prüft beim Aufruf gar nicht.
int main(void)Der Compiler liest: „diese Funktion nimmt KEINE Parameter entgegen".
Klare Aussage. Wenn jemand trotzdem Argumente übergibt → Compiler-Fehler.
void hallo(); // Prototyp ohne (void)
void hallo_v(void); // Prototyp mit (void)
hallo(42); // ✗ Compiler sagt NICHTS — gefährlich!
hallo_v(42); // ✓ Compiler-Fehler: zu viele Argumente
(void) ist die saubere FormAlle Funktionen ohne Parameter werden konsequent mit (void) geschrieben.
// ✓ alle drei Funktionen: kein Parameter → (void)
void trennlinie(void) {
printf("-------------------\n");
}
int lies_zahl(void) {
int n;
scanf("%d", &n);
return n;
}
int main(void) { // auch main bekommt (void)
trennlinie();
int x = lies_zahl();
printf("Du gabst %d ein\n", x);
return 0;
}
(void). So denken auch professionelle C-Codebases (Linux-Kernel, glibc) — das ist nicht Geschmackssache.
Parameter sind Eingabe-Variablen — sie machen die Funktion flexibel.
// Begrüßung — jetzt mit Name als Parameter
void begruesse(char name[]) {
printf("Hallo, %s!\n", name);
}
int main(void) {
begruesse("Anna"); // → Hallo, Anna!
begruesse("Bob"); // → Hallo, Bob!
begruesse("Carla"); // → Hallo, Carla!
return 0;
}
char name[])"Anna")void drucke_rechteck(int breite, int hoehe) {
for (int z = 0; z < hoehe; z++) {
for (int s = 0; s < breite; s++) {
printf("*");
}
printf("\n");
}
}
int main(void) {
drucke_rechteck(5, 3); // 5 breit, 3 hoch
printf("---\n");
drucke_rechteck(8, 2);
return 0;
}
drucke_rechteck(5, 3) heißt breite=5, hoehe=3 — nicht umgekehrt. C ordnet die Argumente positionell zu.
void drucke_dreieck(int hoehe) {
for (int i = 1; i <= hoehe; i++) {
for (int j = 0; j < i; j++) {
printf("* ");
}
printf("\n");
}
}
int main(void) {
drucke_dreieck(5);
return 0;
}
*
* *
* * *
* * * *
* * * * *
drucke_dreieck(10) ruft, bekommt ein größeres Dreieck — derselbe Code.
Funktionen können etwas zurückgeben — wie eine Mathe-Funktion: Input rein, Wert raus.
// Rückgabetyp ist int — die Funktion liefert eine Ganzzahl
int quadrat(int x) {
return x * x; // gibt den Wert zurück
}
int main(void) {
int ergebnis = quadrat(5); // ergebnis ist jetzt 25
printf("5 hoch 2 = %d\n", ergebnis);
printf("7 hoch 2 = %d\n", quadrat(7)); // direkt in printf
return 0;
}
return beendet die Funktion sofort. Was nach return kommt, läuft nicht mehr.
// Doppelt so groß
int doppelt(int x) {
return x * 2;
}
// Maximum von zwei Zahlen
int max(int a, int b) {
if (a > b) return a;
return b;
}
// Celsius zu Fahrenheit
double celsius_zu_f(double c) {
return c * 9.0 / 5.0 + 32.0;
}
int main(void) {
printf("Doppelt von 7: %d\n", doppelt(7)); // → 14
printf("Max(8, 3): %d\n", max(8, 3)); // → 8
printf("20°C in F: %.1f\n", celsius_zu_f(20)); // → 68.0
return 0;
}
Ausgabe machen, Stern drucken, etwas zeichnen.
void drucke_zeile(void) {
printf("---\n");
}
Kein return nötig.
Einen Wert berechnen, den jemand anderes weiterverarbeitet.
int summe(int a, int b) {
return a + b;
}
return mit Wert ist Pflicht.
Bei jedem Aufruf bekommt die Funktion ihre eigenen lokalen Variablen in einem neuen „Stockwerk" — dem Stack-Frame.
int quadrat(int x) {
return x * x;
}
int main(void) {
int a = 5;
int b = quadrat(a);
// (1) a wird gelesen, in x kopiert
// (2) quadrat-Frame: x=5, rechnet 25
// (3) return 25 → b wird 25
// (4) quadrat-Frame wird weggeräumt
}
main ruft quadrat — und wartet, bis quadrat fertig ist. Dann macht main mit dem Ergebnis weiter.
Wenn die Funktion einen Parameter ändert, ändert sich der Original-Wert außerhalb nicht.
void verdopple_versuch(int x) {
x = x * 2; // ändert nur die Kopie x
printf("In Funktion: x = %d\n", x);
}
int main(void) {
int a = 5;
verdopple_versuch(a); // Ausgabe: In Funktion: x = 10
printf("Nach Aufruf: a = %d\n", a);
// Ausgabe: Nach Aufruf: a = 5 ← unverändert!
return 0;
}
a in x kopiert. x ist eine eigene Variable in der Funktion. a bleibt unangetastet.
return nutzen, nicht den Parameter ändern.
Variablen innerhalb der Funktion existieren nur dort.
int summe_bis(int n) {
int summe = 0; // lokal! nur hier sichtbar
for (int i = 1; i <= n; i++) {
summe += i;
}
return summe;
}
int main(void) {
printf("%d\n", summe_bis(10)); // → 55
// printf("%d\n", summe); ← FEHLER! summe ist nur in summe_bis sichtbar
}
summe haben — die zwei stören sich nicht. Das macht Programme stabil.
C liest die Datei von oben nach unten. Wenn main oben eine Funktion ruft, die erst unten definiert ist → Fehler.
int main(void) {
printf("%d", quadrat(3));
// ✗ Compiler: kenne ich nicht!
}
int quadrat(int x) {
return x * x;
}
// Prototyp — Signatur ohne Body
int quadrat(int x);
int main(void) {
printf("%d", quadrat(3)); // ✓
}
int quadrat(int x) {
return x * x;
}
#include <stdio.h>
// 1. Prototypen ganz oben
int quadrat(int x);
int max(int a, int b);
// 2. main als zweites — das Leit-Programm
int main(void) {
printf("%d\n", quadrat(5));
printf("%d\n", max(3, 7));
return 0;
}
// 3. Funktions-Definitionen unten
int quadrat(int x) { return x * x; }
int max(int a, int b) { return a > b ? a : b; }
#include <stdio.h>
// Prototypen
int lies_zahl(void);
int ist_gerade(int n);
void drucke_ergebnis(int n, int gerade);
int main(void) {
int n = lies_zahl();
int g = ist_gerade(n);
drucke_ergebnis(n, g);
return 0;
}
int lies_zahl(void) {
int x;
printf("Gib eine Zahl: ");
scanf("%d", &x);
return x;
}
int ist_gerade(int n) {
return n % 2 == 0; // 1 wenn gerade, 0 wenn ungerade
}
void drucke_ergebnis(int n, int gerade) {
if (gerade) printf("%d ist gerade\n", n);
else printf("%d ist ungerade\n", n);
}
main ist 5 Zeilen — liest wie ein Inhaltsverzeichnis. Die Details verstecken sich in den drei Hilfsfunktionen. Genau so soll guter Code aussehen.
berechne_durchschnittdrucke_dreieckist_primzahllies_zahlDer Name sagt sofort, was die Funktion tut.
do_itf1, test, tempprocess_stuffgoDrei Wochen später versteht niemand mehr, was gemeint war — du selbst auch nicht.
drucke_dreieck ist klar — dreieck oder d nicht.
Funktion oben aufgerufen, unten definiert, kein Prototyp → Compiler-Warnung oder -Fehler.
Funktion mit Rückgabetyp ohne return → undefiniertes Verhalten.
drucke_rechteck(3, 5) statt (5, 3) → Ergebnis falsch.
In main auf eine Variable zugreifen wollen, die nur in einer anderen Funktion existiert → Fehler.
Erwarten, dass die Änderung in der Funktion außen sichtbar wird → ist sie nicht (Pass by Value).
Beim Prototyp leere Klammern statt (void) → Compiler prüft nicht.
void plus_eins(int x) {
x = x + 1;
}
int main(void) {
int a = 10;
plus_eins(a);
printf("%d\n", a);
return 0;
}
plus_eins erhöht seine eigene Kopie x auf 11 — aber a in main bleibt 10. Wer wirklich erhöhen will → Rückgabewert nutzen.
void = „nichts zurück" (Effekt). Sonst Rückgabetyp + return.(void) als Parameterliste = „keine Parameter" — sauberer als leere ().main in der Mitte, Definitionen unten.Fragen?
Prof. Dr. Alexandra Mikityuk
HTW Berlin · Büro Raum 308
Tel +49 30 5019-2664
Nächste Woche: switch/case + Algorithmen I