Notes 1
Aktualisiert am 20. Oktober. Seit dem Entwurf haben wir fast jeden Abschnitt überarbeitet; die Seite entspricht jetzt der Vorlesung vom 19. Oktober. Geänderte und neue Absätze haben links einen Strich.
Sie können diese Seite ausdrucken oder mit einem PDF-Drucker in ein PDF umwandeln, um Ihre eigenen Notizen hinzuzufügen.
Sie können bereits C? Schauen Sie sich doch einmal die Shorts zu dieser Vorlesung an. Gerade in den „Drei Extra-Minuten“ gibt es ein paar anspruchsvollere Inhalte.
Willkommen!
- In der ersten Sitzung haben Sie nach Scratch Ihr erstes Programm in C gesehen:
hello.c, geschrieben, kompiliert, ausgeführt und einmal repariert. Das steht in den Notes 0: Maschinencode und Quelltext, Hello World in C, Funktionen, Fehlermeldungen lesen und verstehen und Variablen. - In dieser Sitzung geht es mit C weiter, mit Typen, Bedingungen, Schleifen und eigenen Funktionen.
- Alle grundlegenden Programmierkonzepte, die in Scratch vorgestellt werden, können beim Erlernen einer beliebigen Programmiersprache verwendet werden.
- Heute werden Sie nicht nur lernen, wie man programmiert, sondern auch, wie man guten Code schreibt.
- Die Codequalität kann nach drei Kriterien bewertet werden. Erstens, hinsichtlich der Korrektheit, was die Frage “Läuft der Code wie beabsichtigt?” betrifft. Zweitens kann man sich das Design ansehen, also sich fragen: “Wie gut ist der Code aufgebaut?”. Schließlich bezieht sich die Frage nach dem Stil darauf, wie ästhetisch ansprechend und konsistent der Code geschrieben ist. Beim Programmieren können drei Hauptkategorien von Problemen auftreten:
- Syntaxfehler: Der Code lässt sich nicht ausführen, weil er gegen die Regeln der Programmiersprache verstößt. Beispiele: fehlendes Semikolon, falsche Klammern, Tippfehler in Funktionsnamen. Diese Fehler sind am leichtesten zu finden, weil es Fehlermeldungen gibt (z.B. vom C-Compiler, wie wir heute noch öfter sehen werden).
- Logikfehler: Der Code kompiliertProgramm, das Quellcode in Maschinencode übersetzt; diesen Vorgang nennt man kompilieren. Jedes C-Programm muss kompiliert werden, bevor es laufen kann. Glossar →, aber verhält sich zur Laufzeit nicht wie gewünscht. Beispiel: Das Programm berechnet eine falsche Summe oder beendet sich vorzeitig. Diese Fehler sind schwieriger zu finden, weil das Programm läuft – nur eben falsch.
- Designfehler: Der Code kompiliert und funktioniert korrekt, ist aber schlecht strukturiert. Beispiele: unnötige Wiederholungen, redundante Prüfungen, unklare Variablennamen, fehlende Abstraktion. Diese Fehler sind am schwierigsten zu erkennen, weil das Programm oberflächlich betrachtet “funktioniert”. Aber schlechtes Design macht Code schwer wartbar, fehleranfällig und unverständlich für andere (und für Ihr zukünftiges Ich!).
In diesem Kurs lernen Sie, alle drei Problemkategorien zu erkennen und zu vermeiden.
Nach welchen drei Kriterien bewertet man Codequalität?
Antwort
Korrektheit (läuft der Code wie beabsichtigt?), Design (wie gut ist er aufgebaut?) und Stil (wie ästhetisch und konsistent ist er geschrieben?).
Warum sind Syntaxfehler am leichtesten zu finden?
Antwort
Weil es Fehlermeldungen gibt, etwa vom C-Compiler.
Wie nennt man einen Fehler, bei dem der Code kompiliert, sich zur Laufzeit aber nicht wie gewünscht verhält?
Antwort
Logikfehler.
Warum sind Designfehler am schwierigsten zu erkennen?
Antwort
Weil das Programm oberflächlich betrachtet funktioniert: Es kompiliert und arbeitet korrekt, ist aber schlecht strukturiert.
Anführungszeichen und Backslash: Escape-Sequenzen
In hello.c steht \n für einen Zeilenumbruch. Der Backslash (\) leitet eine Escape-Sequenz ein: Er und das Zeichen dahinter stehen zusammen für ein einziges Zeichen, das man sonst nicht in den Text schreiben könnte. Das braucht man öfter, als man denkt:
- Ein Anführungszeichen im Text.
printf("Heute ist aber "schönes" Wetter\n");lässt sich nicht übersetzen: Das Anführungszeichen vor „schönes“ beendet den Text, der Compiler meldetexpected ')'. Richtig ist\":printf("Heute ist aber \"schönes\" Wetter\n"); - Ein Backslash im Text, etwa in einem Windows-Pfad.
printf("C:\Windows\n");bricht mitunknown escape sequence '\W'ab, denn\Wgibt es nicht. Richtig ist\\:printf("C:\\Windows\n");gibtC:\Windowsaus.
| Sie schreiben | Es erscheint |
|---|---|
\n | ein Zeilenumbruch |
\t | ein Tabulator |
\" | ein Anführungszeichen " |
\\ | ein Backslash \ |
Tückisch wird es, wenn die Escape-Sequenz zufällig existiert. printf("C:\neu\tabelle\n"); übersetzt ohne jede Meldung und gibt C: aus, dann einen Zeilenumbruch, dann eu, einen Tabulator und abelle. Der Compiler kann nicht wissen, dass Sie einen Ordner „neu“ und eine Datei „tabelle“ meinten. Das ist ein Logikfehler (siehe oben): Das Programm läuft, nur nicht so, wie gewünscht.
Was gibt printf("C:\neu\tabelle\n"); aus, und wie schreiben Sie es richtig?
Antwort
C:, dann einen Zeilenumbruch, dann eu, einen Tabulator und abelle: \n und \t sind Escape-Sequenzen. Für einen Backslash im Text schreiben Sie \\, also printf("C:\\neu\\tabelle\n");.
Aufwärmübungen: Scratch-Programme tracen
Bevor Sie mit C weitermachen, können Sie das Wissen aus der letzten Woche mit zwei Tracing-Übungen in Scratch auffrischen. Beide sind freiwillig und zum Selbermachen gedacht: Überlegen Sie jeweils auf Papier, was das Programm tun wird, und klicken Sie erst danach auf die grüne Flagge.
Pen Down/Pen Up: Bauen Sie ein Programm, das in einer Schleife abwechselnd mit
pen downein Stück zeichnet und mitpen upein Stück weitergeht (dafür brauchen Sie die Pen-Erweiterung, die Sie mit dem Icon links unten aktivieren). Sagen Sie vorher, was die Katze zeichnet: Es entsteht eine gestrichelte Linie, denn der „Stift“ zeichnet nur, solange er unten ist. Drehen Sie die Katze danach vor der Schleife um 45 Grad und sagen Sie wieder vorher, was herauskommt.Das 3×3-Raster-Problem: Schreiben Sie mit zwei verschachtelten
repeat-Schleifen ein Programm, das ein Raster aus 3 × 3 Punkten zeichnet, indem es die x- und die y-Koordinate der Katze ändert. Ein typischer Fehler dabei: Nach der inneren Schleife muss x wieder auf den Anfangswert gesetzt werden, bevor die äußere Schleife weiterläuft. Sonst steht die zweite Reihe nicht unter der ersten. Gehen Sie Ihr Programm Zeile für Zeile durch und notieren Sie nach jedem Block, wo die Katze steht.
So üben Sie algorithmisches Denken: Man führt Code im Kopf oder auf Papier Schritt für Schritt aus, um zu verstehen, was passiert. Diese Fähigkeit werden Sie auch in C ständig brauchen.
Was bedeutet es, ein Programm zu „tracen“?
Antwort
Man führt den Code mental oder auf Papier Schritt für Schritt aus und verfolgt dabei den Zustand der Variablen mit.
Variablen und Datentypen
Als Nächstes schreiben wir ein Programm, das zwei Zahlen einliest und sagt, welche größer ist. Dafür brauchen wir einen Platz für jede Zahl: eine Variable, wie in Scratch.
In C kann man einem
int, also einer Variable, die ganze Zahlen aufnimmt, wie folgt einen Wert zuweisen:int counter = 0;Dadurch weisen wir der Variablen
countervom Typintden Wert0zu.Nun können wir zu
countereine Eins addierencounter = counter + 1;Dadurch wird 1 zum Wert von
counterhinzugefügt.Dies kann auch kürzer dargestellt werden:
counter += 1;Oder noch kürzer:
counter++;Beachten Sie, dass auch diese Anweisung “1” zum Wert von
counteraddiert, aber deutlich kürzer ist.Sie können auch “1” subtrahieren, z.B. so:
counter--;Beachten Sie, dass “1” vom Wert von
counterabgezogen wird.Mit Datentypen legen wir beim Programmieren fest, wie die Bits, die in einer Variablen gespeichert werden, interpretiert werden sollen. In der allerersten Vorlesung hatten wir gesehen, dass eine bestimmte Bitfolge sehr unterschiedlich interpretiert werden kann, je nachdem ob es eine Zahl, ein Buchstabe oder eine Farbe ist.
Für den Anfang genügen vier Typen:
int, eine ganze Zahl wie42oder-3char, ein einzelnes Zeichen wieaoder2string, eine Zeichenkette wie"Hallo"bool, ein Wahrheitswert: entweder wahr (true) oder falsch (false)
Weitere Typen (
long,floatunddouble) lernen Sie am Ende dieser Vorlesung kennen, zusammen mit den Grenzen, an dieintstößt (siehe Typen).
Was bewirkt die Anweisung counter++;?
Antwort
Sie addiert 1 zum Wert von counter, genau wie counter = counter + 1; oder counter += 1;.
Was legen Sie mit einem Datentyp fest?
Antwort
Wie die Bits, die in einer Variablen gespeichert sind, interpretiert werden sollen.
Die Manual Pages: Ihre Dokumentation beim Programmieren
Wenn Sie nicht genau wissen, wie eine Funktion funktioniert oder welche Parameter sie benötigt, hilft Ihnen die offizielle Dokumentation. Für diesen Kurs sind die CS50 Manual Pages besonders nützlich:
Speziell für Anfänger: Diese Manual Pages sind genau für Menschen wie Sie gemacht, die gerade C lernen. Sie enthalten weniger technischen Ballast als die offizielle C-Dokumentation.
Der “less comfortable” Modus: Auf jeder Seite finden Sie einen Schiebeschalter mit “less comfortable”. Standardmäßig ist dieser aktiviert und blendet komplizierte technische Details aus. Später im Semester können Sie ihn ausschalten und die “volle Wahrheit” sehen.
Was Sie dort finden: Für jede Funktion (wie
printf,get_string, etc.) finden Sie:- Was die Funktion tut
- Welche Parameter sie erwartet
- Was sie zurückgibt
- Beispielcode
Tipp: Machen Sie sich mit den Manual Pages vertraut – professionelle Programmierer verbringen viel Zeit damit, Dokumentation zu lesen!
Wo schlagen Sie im Kurs nach, was eine Funktion tut, welche Parameter sie erwartet und was sie zurückgibt?
Antwort
In den CS50 Manual Pages (manual.cs50.io).
Formatcodes
printfkennt viele Formatcodes. Für die Typen, die wir bisher kennen, sind es diese (die Codes fürlong,floatunddoublefolgen bei den Typen am Ende):%c für char (einzelnes Zeichen) %i für int (Ganzzahl) %s für string (Zeichenkette)%swird für String-Variablen verwendet.%iwird fürintoder Integer-Variablen verwendet. Sie können mehr darüber auf den Manual Pages herausfinden.
Mehrere Platzhalter verwenden
Sie können beliebig viele Formatcodes in einem printf-Aufruf verwenden. Die Werte werden in der Reihenfolge eingesetzt, in der sie nach dem Format-String angegeben werden:
string name1 = get_string("Erster Name: ");
string name2 = get_string("Zweiter Name: ");
printf("Hello, %s and %s\n", name1, name2);Dies würde zum Beispiel Hello, Maria and Josef ausgeben, wenn die entsprechenden Namen eingegeben wurden.
Sie können auch verschiedene Typen kombinieren:
char initial = 'M';
int age = 25;
printf("Initial: %c, Age: %i\n", initial, age);Wichtig: Die Anzahl der Formatcodes muss exakt mit der Anzahl der Argumente übereinstimmen. Geben Sie zu viele oder zu wenige Argumente an, bricht der Compiler mit einer Fehlermeldung ab.
Bedingte Anweisungen
- Ein weiterer Baustein, den Sie in Scratch verwendet haben, waren die Bedingten Anweisungen. Man möchte zum Beispiel etwas tun, wenn x größer als y ist. Außerdem möchte man vielleicht etwas anderes tun, wenn diese Bedingung nicht erfüllt ist.
- Wir sehen uns ein paar Beispiele aus Scratch an.
- Mit zwei Variablen vom Typ
int, die Sie mitget_intfüllen, können Sie Ihre erste bedingte Anweisung programmieren. - Geben Sie im Terminalfenster
code compare.cein und schreiben Sie folgenden Code:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int x = get_int("What's x? ");
int y = get_int("What's y? ");
if (x < y)
{
printf("x is less than y\n");
}
}Beachten Sie, dass wir zwei Variablen erstellen, einen
int(Ganzzahl) namensxund einen weiteren namensy. Die Werte dieser Variablen werden mit der Funktionget_inteingelesen.Sie können Ihren Code ausführen, indem Sie
make compareim Terminalfenster ausführen, gefolgt von./compare. Wenn Sie eine Fehlermeldung erhalten, überprüfen Sie Ihren Code auf Fehler.Wir schauen uns in der Vorlesung Flussdiagramme an; damit kann man die Funktionsweise eines Computerprogramms zu untersuchen und prüfen, ob das Design des Codes gut ist.
Wenn wir uns ein Flussdiagramm des obigen Codes ansehen, können wir zahlreiche Mängel feststellen.
Wir können Ihr Programm verbessern, indem wir es wie folgt ändern:
#include <cs50.h> #include <stdio.h> int main(void) { int x = get_int("What's x? "); int y = get_int("What's y? "); if (x < y) { printf("x is less than y\n"); } else if (x > y) { printf("x is greater than y\n"); } else { printf("x is equal to y\n"); } }Beachten Sie, dass nun alle möglichen Ergebnisse berücksichtigt werden.
Sie können Ihr Programm nun erneut kompilieren, ausführen und testen.
Im Flussdiagramm sehen wir, dass der Code nun ein besseres Design hat.
Was ist bei compare.c besser an der Fassung mit if, else if und else als an der mit nur if (x < y)?
Antwort
Es werden alle möglichen Ergebnisse berücksichtigt: kleiner, größer und gleich.
Wozu schauen wir uns Flussdiagramme an?
Antwort
Um die Funktionsweise eines Programms zu untersuchen und zu prüfen, ob das Design des Codes gut ist.
Mit welcher Funktion der CS50-Bibliothek lesen Sie eine Ganzzahl (int) vom Benutzer ein?
Antwort
get_int (aus cs50.h).
Übung: Bedingungen in drei Stufen
Drei Aufgaben zum selben Thema. Wählen Sie die Stufe, die Sie schaffen; wer fertig ist, nimmt die nächste. Versuchen Sie es erst selbst und klappen Sie die Lösung danach auf.
- Stufe 1: Ergänzen Sie
compare.cso, dass bei gleichen Zahlenx is equal to yerscheint. Übersetzen Sie das Programm und testen Sie es mit 2 und 2. Die Lösung ist das Programm oben mitif,else ifundelse. - Stufe 2: Schreiben Sie ein Programm
sign.c, das eine ganze Zahl einliest undpositiv,negativodernullausgibt. - Stufe 3: Schreiben Sie ein Programm
max.c, das drei ganze Zahlen einliest und die größte ausgibt.
Lösung zu Stufe 2
Das Muster ist dasselbe wie in compare.c: zwei Fragen, der dritte Fall steht im else.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n = get_int("Zahl: ");
if (n > 0)
{
printf("positiv\n");
}
else if (n < 0)
{
printf("negativ\n");
}
else
{
printf("null\n");
}
}Eine Lösung zu Stufe 3
Die Variable max merkt sich die größte Zahl, die das Programm bisher gesehen hat. Am Anfang ist das a; ist b oder c größer, wird max überschrieben. Es gibt andere Lösungen, etwa mit &&.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int a = get_int("a: ");
int b = get_int("b: ");
int c = get_int("c: ");
int max = a;
if (b > max)
{
max = b;
}
if (c > max)
{
max = c;
}
printf("%i\n", max);
}Optionale geschweifte Klammern (wichtig zu wissen!)
In C können die geschweiften Klammern bei if, else if, else, while und for weggelassen werden, wenn nur eine einzige Anweisung folgt. Das bedeutet, folgender Code ist syntaktisch korrekt:
if (x < y)
printf("x is less than y\n");
else
printf("x is not less than y\n");Aber: In diesem Kurs werden wir immer geschweifte Klammern verwenden, auch bei nur einer Anweisung, weil:
- Es ist übersichtlicher und weniger fehleranfällig
- Wenn Sie später eine zweite Anweisung hinzufügen, vergessen Sie vielleicht die Klammern
- Es ist ein verbreiteter Stil in der professionellen Softwareentwicklung
Sie werden Code ohne geschweifte Klammern im Internet finden – jetzt wissen Sie, warum das funktioniert. Aber schreiben Sie Ihren eigenen Code bitte immer mit geschweiften Klammern!
- Lassen Sie uns einen anderen Datentyp namens
charbetrachten. - Wir schreiben ein neues Programm, indem wir
code agree.cin das Terminalfenster eingeben. - Während ein
stringeine Reihe von Zeichen ist, ist eincharein einzelnes Zeichen (engl. character).
Schreiben Sie im Texteditor folgenden Code:
#include <cs50.h> #include <stdio.h> int main(void) { // Prompt user to agree char c = get_char("Do you agree? "); // Check whether agreed if (c == 'Y' || c == 'y') { printf("Agreed.\n"); } else if (c == 'N' || c == 'n') { printf("Not agreed.\n"); } }Beachten Sie, dass bei
charimmer einfache (nicht doppelte) Anführungszeichen verwendet werden. Beachten Sie außerdem, dass==prüft, dass etwas gleich mit etwas anderem ist, während ein einzelnes Gleichheitszeichen in C eine ganz andere Funktion hätte (nämlich die Zuweisung von Werten zu Variablen).Das
||bedeutet so viel wie “oder”: Mindestens eine der beiden Bedingungen ist wahr. Das Gegenstück ist&&(“und”): Beide Bedingungen sind wahr.Weitere Vergleiche bestehen ebenfalls aus zwei Zeichen, das Gleichheitszeichen steht hinten:
<=(kleiner oder gleich),>=(größer oder gleich) und!=(ungleich).Schreiben Sie im
ifversehentlich nur ein=, brichtmakein unserer Umgebung mit einer Fehlermeldung ab und schlägt==vor.Sie können Ihren Code testen, indem Sie
make agreein das Terminalfenster eingeben, gefolgt von./agree.
Warum setzen wir im Kurs trotzdem immer geschweifte Klammern, auch bei nur einer Anweisung?
Antwort
Es ist übersichtlicher und weniger fehleranfällig: Fügt man später eine zweite Anweisung hinzu, vergisst man sonst leicht die Klammern.
Was unterscheidet einen char von einem string?
Antwort
Ein char ist ein einzelnes Zeichen, ein string eine Reihe von Zeichen.
Welche Anführungszeichen verwenden Sie in C für einen char?
Antwort
Einfache Anführungszeichen, zum Beispiel 'y'.
Was ist in C der Unterschied zwischen == und =?
Antwort
== prüft auf Gleichheit, = weist einer Variablen einen Wert zu.
Was bedeutet der Operator || in C?
Antwort
„oder“.
Übung: Parsons-Problem
Eine gute Methode, um Ihr Verständnis für C-Code zu testen, ist ein Parsons-Problem (nach Dale Parsons): Code-Zeilen sind durcheinandergeraten, und Sie bringen sie in die richtige Reihenfolge.
Aufgabe: Das Programm liest eine Temperatur in °C ein und gibt aus, ob es “kalt” (unter 10°), “warm” (10–25°) oder “heiß” (über 25°) ist. Der Rahmen steht schon da:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
// hier fehlen die Zeilen
}Bringen Sie die folgenden Zeilen in die richtige Reihenfolge und notieren Sie die Lösung als Folge von Buchstaben. Die geschweiften Klammern um jedes printf denken Sie sich dazu. Achtung: Drei Zeilen sind falsch und bleiben übrig.
A if (temp < 10)
B printf("warm\n");
C else if (temp >= 25)
D int temp = get_int("Temperatur: ");
E if (temp <= 10)
F printf("kalt\n");
G else
H string temp = get_int("Temperatur: ");
I else if (temp > 25)
J printf("heiß\n");Tipp: Achten Sie auf die Grenzen der Bereiche (was ist bei genau 10°, was bei genau 25°?) und auf den Typ der Variable.
Lösung: D, A, F, I, J, G, B. Übrig bleiben E, C und H:
- E
if (temp <= 10): Bei genau 10° soll “warm” herauskommen, nicht “kalt”. - C
else if (temp >= 25): Bei genau 25° soll “warm” herauskommen, nicht “heiß”. - H
string temp = get_int("Temperatur: ");:get_intgibt einenintzurück, keinenstring.makebricht hier ab.
Fehler an den Grenzen sind die häufigsten. Die Mitte steht im else: Was nicht kalt und nicht heiß ist, ist warm. Das ganze Programm mit den Klammern:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int temp = get_int("Temperatur: ");
if (temp < 10)
{
printf("kalt\n");
}
else if (temp > 25)
{
printf("heiß\n");
}
else
{
printf("warm\n");
}
}Schleifen
Dem Schleifen-Baustein aus Scratch können wir auch in unseren C-Programmen verwenden.
Wir sehen uns ein paar Beispiele aus Scratch an. Betrachten Sie den folgenden Code:
int counter = 3; while (counter > 0) { printf("meow\n"); counter = counter - 1; }Beachten Sie, dass der Code der Variablen
counterden Wert “3” zuweist. Dann wird in der while-Schleife “meow” ausgegeben und bei jeder Iteration der Wert “1” vom Zähler abgezogen. Sobald der Zähler nicht mehr größer als Null ist, endet die Schleife.Geben Sie in Ihrem Terminal-Fenster
code meow.cein und schreiben Sie folgenden Code:
#include <stdio.h>
int main(void)
{
printf("meow\n");
printf("meow\n");
printf("meow\n");
}- Beachten Sie, dass dies wie beabsichtigt funktioniert, aber es Potenzial für besseres Design gibt.
- Wir können unser Programm verbessern, indem wir Ihren Code wie folgt abändern:
#include <stdio.h>
int main(void)
{
int i = 3;
while (i > 0)
{
printf("meow\n");
i--;
}
}Beachten Sie, dass wir einen
intnamensierstellen und ihm den Wert “3” zuweisen. Dann erstellen wir eine while-Schleife, die läuft, solangei > 0erfüllt ist. Dann wird der Schleifenkörper durchlaufen. Jedes Mal wird “1” vonimit der Anweisungi--abgezogen.In ähnlicher Weise können wir es mit Hochzählen implementieren:
#include <stdio.h> int main(void) { int i = 1; while (i <= 3) { printf("meow\n"); i++; } }Beachten Sie, dass unser Zähler
ibei1beginnt. Jedes Mal, wenn die Schleife läuft, wird der Zähler um “1” erhöht. Sobald der Zähler größer als “3” ist, wird die Schleife angehalten.In der Informatik zählen wir in der Regel von Null an. Am besten überarbeiten Sie Ihren Code daher wie folgt:
#include <stdio.h> int main(void) { int i = 0; while (i < 3) { printf("meow\n"); i++; } }Beachten Sie, dass wir jetzt von Null an zählen.
Ein weiteres Werkzeug in unserem Werkzeugkasten für Schleifen ist eine for-Schleife.
Wir können das Design unseres Programms meow.c mit einer for-Schleife weiter verbessern. Ändern Sie Ihren Code dazu wie folgt:
#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("meow\n"); } }Beachten Sie, dass die for-Schleife in ihren runden Klammern drei Teile enthält. Der erste Teil
int i = 0startet unseren Zähler bei Null. Der zweite Teili < 3ist die Bedingung, die überprüft wird. Schließlich weist der dritte Teili++die Schleife an, bei jedem Schleifendurchlauf um eins zu erhöhen.Mit dem folgenden Code können wir sogar eine Endlosschleife erstellen:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
while (true)
{
printf("meow\n");
}
}Beachten Sie, dass
truedie Bedingung immer erfüllt. Daher wird der Code in der Schleife immer wieder ausgeführt. Wenn Sie dieses Programm starten, verlieren Sie die Kontrolle über Ihr Terminalfenster. Sie können das Programm abbrechen, indem SieControl-C(oderStrg-C) auf Ihrer Tastatur drücken.Später werden wir es uns noch genauer ansehen, aber eine eigene Funktion können Sie in C auch jetzt schon wie folgt erstellen:
void meow(void) { printf("meow\n"); }Das anfängliche
voidbedeutet, dass die Funktion keine Werte zurückgibt. Das(void)bedeutet, dass der Funktion keine Werte übergeben werden.
Diese Funktion kann in der Hauptfunktion wie folgt verwendet werden:
#include <stdio.h> void meow(void); int main(void) { for (int i = 0; i < 3; i++) { meow(); } } void meow(void) { printf("meow\n"); }Beachten Sie, dass die Funktion “meow” mit der Anweisung “meow()” aufgerufen wird. Dies ist möglich, weil die “meow”-Funktion am Ende des Codes definiert ist und der Prototyp der Funktion am Anfang des Codes als
void meow(void)angegeben ist.Warum nicht einfach
meowübermainschreiben? Das ginge, abermainsoll oben stehen, denn sie ist der Einstiegspunkt, und wer den Code liest, will sie sofort sehen. Der Compiler liest den Code allerdings von oben nach unten. Ohne Prototyp kennt ermeownoch nicht, wenn er inmainauf den Aufruf stößt, und meldet einen Fehler.
Ihre “meow”-Funktion kann weiter modifiziert werden, um Eingaben zu akzeptieren:
#include <stdio.h> void meow(int n); int main(void) { meow(3); } // Meow some number of times void meow(int n) { for (int i = 0; i < n; i++) { printf("meow\n"); } }Beachten Sie, dass der Prototyp in
void meow(int n)geändert wurde, um den Compiler zu informieren, dassmeoweinenintals Eingabe akzeptiert.
Was bedeuten die drei Teile von for (int i = 0; i < 3; i++)?
Antwort
int i = 0: startet den Zähler bei 0i < 3: die Bedingung, die geprüft wirdi++: erhöht den Zähler bei jedem Durchlauf um eins
Wie schreiben Sie in C eine Endlosschleife?
Antwort
while (true): Die Bedingung ist immer erfüllt.
Wie brechen Sie ein Programm ab, das im Terminal endlos läuft?
Antwort
Mit Strg-C (Control-C).
Was bedeutet das erste void in void meow(void)?
Antwort
Die Funktion gibt keinen Wert zurück.
Was bedeutet (void) in void meow(void)?
Antwort
Der Funktion werden keine Werte übergeben.
Warum schreibt man main meist oben in die Datei und nicht ans Ende?
Antwort
main ist der Einstiegspunkt. Wer den Code liest, will ihn sofort sehen. Funktionen, die darunter stehen, macht ein Prototyp dem Compiler bekannt.
Wozu steht void meow(void); oben in der Datei, wenn meow erst am Ende definiert wird?
Antwort
Das ist der Prototyp: Er macht die Funktion dem Compiler bekannt, sodass main sie aufrufen kann, obwohl die Definition erst am Ende steht.
Operatoren und Abstraktion
- Sie können einen Taschenrechner in C implementieren.
Geben Sie in Ihrem Terminal
code calculator.cein und schreiben Sie folgenden Code:#include <cs50.h> #include <stdio.h> int main(void) { // Prompt user for x int x = get_int("x: "); // Prompt user for y int y = get_int("y: "); // Perform addition printf("%i\n", x + y); }Beachten Sie, wie die Funktion
get_intverwendet wird, um zweimal eine Zahl vom Benutzer zu erhalten. Eine Ganzzahl wird in derint-Variablenxgespeichert, eine weitere in derint-Variableny. Dann druckt die Funktionprintfden Wert vonx + yaus, und zwar an die Stelle, an der der Formatcode%isteht. Danach wird mit\nein Zeilenumbruch ausgegeben.Das
+ist ein Operator. In C gibt es insbesondere folgende arithmetische Operatoren:+für Addition-für Subtraktion*für Multiplikation/für Division%für Modulo (Rest nach einer Division)
/und%gehören oft zusammen. Angenommen, Sie schulden jemandem 97 Cent und wollen möglichst wenige Euro-Münzen herausgeben (50, 20, 10, 5, 2 und 1 Cent).97 / 50ergibt1: Teilt man eine ganze Zahl durch eine ganze Zahl, fallen die Nachkommastellen weg. Also ein 50er.97 % 50ergibt47: der Rest, der noch herauszugeben ist. Mit der nächsten Münze geht es genauso weiter:47 / 20ergibt2, also zwei 20er, Rest7;7 / 10ergibt0, also kein 10er; ein 5er, Rest 2; ein 2er. Zusammen 5 Münzen: 50, 20, 20, 5 und 2 Cent. Genauso rechnet die Aufgabe Bargeld aus Übung 1, dort mit US-Münzen zu 25, 10, 5 und 1 Cent.
Was ergeben 47 / 20 und 47 % 20?
Antwort
47 / 20 ergibt 2 (ganze Zahl durch ganze Zahl, die Nachkommastellen fallen weg), 47 % 20 ergibt 7 (der Rest).
Indem wir unser zuvor erworbenes Wissen über Funktionen anwenden, können wir die Addition in eine Funktion abstrahieren. Ändern Sie Ihren Code dazu wie folgt:
- Abstraktion ist die Kunst, unseren Code so zu vereinfachen, dass wir Probleme schrittweise in kleinere Teile zerlegen und die Details der Lösung vor uns verbergen.
#include <cs50.h> #include <stdio.h> int add(int a, int b); int main(void) { // Prompt user for x int x = get_int("x: "); // Prompt user for y int y = get_int("y: "); // Perform addition int z = add(x, y); printf("%i\n", z); } int add(int a, int b) { int c = a + b; return c; }Beachten Sie, dass die Funktion
addzwei Variablen als Eingabe benötigt. Diese Werte werden den Variablenaundbzugewiesen. Die Funktion führt dann die Addition durch und speichert das in der Variablenc. Danach gibt sie den Wert inczurück dorthin, wo sie aufgerufen wurde.
Was berechnet der Operator % in C?
Antwort
Den Rest nach einer Division (Modulo).
Was ist Abstraktion beim Programmieren?
Antwort
Probleme schrittweise in kleinere Teile zerlegen und die Details der Lösung verbergen, etwa in einer Funktion wie add.
Was bewirkt return c; am Ende der Funktion add?
Antwort
Es gibt den Wert von c dorthin zurück, wo die Funktion aufgerufen wurde.
Scope: Variablen leben nur in ihrer Funktion
Ein wichtiges Konzept in C ist Scope (deutsch: Gültigkeitsbereich) – der Kontext, in dem Variablen existieren:
Jede Funktion ist ihre eigene “Box”: Die Variable
xexistiert nur inmain, die Variablecexistiert nur inadd. Die Funktionaddhat keine Ahnung, dassxinmainexistiert.Werte werden kopiert, nicht geteilt: Wenn Sie
add(x, y)aufrufen, werden die Werte vonxundykopiert und in den Parameternaundbgespeichert. Die Variablenxundyselbst werden nicht “gesendet”. Es sind komplett neue Variablen mit kopierten Werten.Was in der Funktion passiert, bleibt in der Funktion: Die Variablen
a,bundcin der Funktionaddexistieren nur, während die Funktion läuft. Sobald die Funktion endet und einen Wert zurückgibt, werden diese Variablen zerstört – sie existieren nicht mehr. Alles, was bleibt, ist der Rückgabewert.Originalvariablen bleiben unverändert: Die Variablen
xundyinmainbehalten ihre Werte, egal was inaddpassiert. Sie haben niemals mitbekommen, dassaddaufgerufen wurde.Genauer gilt das für jeden Block: Eine Variable lebt bis zur schließenden geschweiften Klammer des Blocks, in dem sie angelegt wurde. Das
ieinerfor-Schleife gibt es hinter ihrer schließenden Klammer nicht mehr. Das brauchen Sie gleich bei der Eingabevalidierung.
Stellen Sie sich Funktionen wie getrennte Räume vor: Jede Funktion hat ihre eigenen Variablen und kann nicht einfach die Variablen einer anderen Funktion sehen oder ändern. Kommunikation zwischen Funktionen erfolgt nur durch Parameter (Eingabe) und Return-Werte (Ausgabe).
Das Design dieses Programms kann wie folgt weiter verbessert werden:
#include <cs50.h> #include <stdio.h> int add(int a, int b); int main(void) { // Prompt user for x int x = get_int("x: "); // Prompt user for y int y = get_int("y: "); // Perform addition printf("%i\n", add(x, y)); } int add(int a, int b) { return a + b; }Beachten Sie, dass “c” in der Funktion “add” entfernt wurde.
Obwohl es sehr nützlich ist, zu einer “Add”-Funktion zu abstrahieren, ist es etwas umständlich. Sie könnten die Addition auch direkt im Aufruf von
printfdurchführen:#include <cs50.h> #include <stdio.h> int main(void) { // Benutzer zur Eingabe von x auffordern int x = get_int("x: "); // Benutzer zur Eingabe von y auffordern int y = get_int("y: "); // Addition durchführen printf("%i\n", x + y); }Beachten Sie, dass die Addition innerhalb des Aufrufs von
printfdurchgeführt wird.In ähnlicher Weise könnten auch andere Operationen, etwa eine Division durchgeführt werden:
#include <cs50.h> #include <stdio.h> int main(void) { // Aufforderung an den Benutzer zur Eingabe von x int x = get_int("x: "); // Aufforderung an den Benutzer zur Eingabe von y int y = get_int("y: "); // Teilen Sie x durch y printf("%i\n", x / y); }
Was bezeichnet der Begriff „Scope“ (Gültigkeitsbereich)?
Antwort
Den Kontext, in dem eine Variable existiert. In C: Variablen leben nur in ihrer Funktion.
Was passiert beim Aufruf add(x, y) mit x und y?
Antwort
Ihre Werte werden kopiert und in den Parametern a und b gespeichert. x und y selbst werden nicht „gesendet“ und bleiben unverändert.
Was geschieht mit den Variablen a, b und c der Funktion add, sobald die Funktion endet?
Antwort
Sie werden zerstört. Übrig bleibt nur der Rückgabewert.
Über welche zwei Wege kommunizieren Funktionen in C miteinander?
Antwort
Über Parameter (Eingabe) und Rückgabewerte (Ausgabe).
Linux und die Kommandozeile
- LinuxEin Betriebssystem wie Windows oder macOS. Die CS50-Umgebung und die Prüfungslaptops laufen mit Linux. Glossar → ist ein Betriebssystem, bei dem die Kommandozeile eine wichtige Rolle spielt. Wenn wir das Terminalfenster von VS Code im Kurs benutzen, steuern wir einen Rechner, auf dem Linux läuft – entweder in der Cloud oder in einem Container, der auf Ihrem Laptop gestartet wird, wenn Sie VS Code öffnen.
- Einige übliche Befehlszeilenprogramme, die wir verwenden können, sind:
cdWechselt das Verzeichnis:cd megeht in den Ordnerme,cd ..eine Ebene nach oben,cdallein ins Homeverzeichnis. Glossar →, um unser aktuelles Verzeichnis (Ordner) zu wechselncp, um Dateien und VerzeichnisseAnderes Wort für Ordner. Glossar → zu kopierenlsListet die Dateien und Ordner im aktuellen Verzeichnis auf. Glossar →, zum Auflisten von Dateien in einem VerzeichnismkdirLegt einen neuen Ordner an:mkdir meerzeugt den Ordnerme. Glossar →, um ein Verzeichnis zu erstellenmv, zum Verschieben (Umbenennen) von Dateien und VerzeichnissenrmLöscht Dateien. Es gibt keinen Papierkorb; was mitrmgelöscht ist, lässt sich nicht wiederherstellen. Glossar →, zum Entfernen (Löschen) von Dateienrmdir, um Verzeichnisse zu entfernen (zu löschen)
- Das am häufigsten verwendete Programm ist
ls, das alle Dateien im aktuellen Verzeichnis auflistet. Geben Sie einfachlsin das Terminalfenster ein und drücken SieEnter. Sie werden alle Dateien im aktuellen Verzeichnis sehen. - Ein weiterer nützlicher Befehl ist
mv, mit dem Sie eine Datei in ein anderes Verzeichnis verschieben oder umbenennen können. Zum Beispiel können Sie diesen Befehl verwenden, umHello.c(beachten Sie das großeH) inhello.cumzubenennen, indem Siemv Hello.c hello.ceingeben. - Sie können auch Verzeichnisse erstellen. Sie können
mkdir pset1eingeben, um ein Verzeichnis namenspset1zu erstellen. - Sie können dann
cd pset1benutzen, um von Ihrem aktuellen Verzeichnis in das neu angelegte Verzeichnispset1zu wechseln. - In welchem Verzeichnis Sie sind, sehen Sie – je nach Konfiguration – am Zeilenanfang. Wenn Sie sich unsicher sind, können Sie mit
pwd(von engl. print working directory) Ihr aktuelles Verzeichnis ausgeben lassen.
Mit welchem Befehl listen Sie die Dateien im aktuellen Verzeichnis auf?
Antwort
ls.
Mit welchem Befehl wechseln Sie in ein anderes Verzeichnis?
Antwort
cd, zum Beispiel cd pset1.
Mit welchem Befehl erstellen Sie auf der Kommandozeile ein neues Verzeichnis?
Antwort
mkdir, zum Beispiel mkdir pset1.
Mit welchem Befehl verschieben oder benennen Sie auf der Kommandozeile eine Datei um?
Antwort
mv, zum Beispiel mv Hello.c hello.c.
Mit welchem Befehl kopieren Sie auf der Kommandozeile Dateien und Verzeichnisse?
Antwort
cp.
Mit welchem Befehl löschen Sie auf der Kommandozeile eine Datei?
Antwort
rm (für Verzeichnisse: rmdir).
Mit welchem Befehl lassen Sie sich auf der Kommandozeile Ihr aktuelles Verzeichnis ausgeben?
Antwort
pwd (von engl. print working directory).
Terminal-Effizienz: Die Werkzeuge der Profis
Professionelle Programmiererinnen und Programmierer verbringen viel Zeit im Terminal. Daher gibt es einige Techniken, die Ihre Arbeit deutlich effizienter machen:
Tab-Vervollständigung: Wenn Sie einen Dateinamen oder Befehl eingeben, drücken Sie die Tab-Taste. Das Terminal vervollständigt automatisch, was Sie schreiben wollten. Wenn es mehrere Möglichkeiten gibt, zeigt ein zweites Drücken von Tab alle Optionen an. Beispiel: Tippen Sie
mv mund drücken Sie Tab – das Terminal hilft Ihnen, den Rest zu finden.Befehlshistorie mit Pfeiltasten: Mit Pfeil nach oben und Pfeil nach unten können Sie durch zuvor eingegebene Befehle navigieren. Das erspart Ihnen, lange Befehle immer wieder neu einzutippen.
Terminal aufräumen mit
clear: Der Befehlclear(oder die Tastenkombination Ctrl+L bzw. Strg+L) leert den Bildschirm und gibt Ihnen wieder Übersicht. Viele Programmierer machen das regelmäßig, um ein “aufgeräumtes Terminal” zu haben.
Diese kleinen Tricks sparen enorm viel Zeit – professionelle Entwickler tippen oft nur die Hälfte ihrer Befehle selbst, den Rest erledigen Tab-Vervollständigung und die Befehlshistorie.
Mit welchem Befehl leeren Sie den Bildschirm des Terminals?
Antwort
clear (oder die Tastenkombination Strg+L).
Mario
- Alles, was wir heute besprochen haben, sind Bausteine für Ihre Arbeit als angehende Informatikerinnen und Informatiker.
- Die folgenden Ausführungen sollen Ihnen helfen, sich auf die Arbeit an einer Problemstellung für diesen Kurs im Allgemeinen einzustellen: Wie geht man an ein Problem aus der Informatik heran?
- Stellen Sie sich vor, wir wollten die Grafik des Spiels Super Mario Bros. nachbilden. Wie müsste ein Programm aussehen, das so etwas wie die vier horizontalen Fragezeichenblöcke ausgibt?
Progressives Refactoring: Code Schritt für Schritt verbessern
In den folgenden Beispielen werden wir einen wichtigen Ansatz der Softwareentwicklung kennenlernen: progressives Refactoring. Wir beginnen mit einer einfachen, aber nicht optimalen Lösung und verbessern sie Schritt für Schritt. Bei jedem Schritt fragen wir uns: Welches Problem behebt diese Änderung? Warum ist der Code jetzt besser?
Sie werden sehen, wie wir den Code mehrfach überarbeiten:
- Von hartkodiertem Code zu Schleifen (weniger Wiederholung)
- Von einfachen Schleifen zu verschachtelten Schleifen (komplexere Muster)
- Von “magischen Zahlen” zu Variablen (bessere Lesbarkeit)
- Von Variablen zu Konstanten (verhindert ungewollte Änderungen)
- Von festen Werten zu Benutzereingaben (Flexibilität)
- Von unvalidiertem Input zu Eingabeprüfung (Robustheit)
- Von Code-Duplikation zu eleganten Lösungen (besseres Design)
Dieser schrittweise Ansatz ist typisch für gutes Programmieren: Erst etwas zum Laufen bringen, dann systematisch verbessern.

Geben Sie im Terminalfenster
code mario.cein und programmieren Sie Folgendes:#include <stdio.h> int main(void) { for (int i = 0; i < 4; i++) { printf("?"); } printf("\n"); }Beachten Sie, dass hier vier Fragezeichen in einer Schleife gedruckt werden.
Mit der gleichen Logik können wir auch drei vertikale Blöcke erstellen.

Mario Blocks
Ändern Sie dazu Ihren Code wie folgt:
#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("#\n"); } }Beachten Sie, dass drei vertikale Ziegelsteine in einer Schleife gedruckt werden.
Was wäre, wenn wir diese Ideen kombinieren wollten, um eine drei-mal-drei-Gruppe von Blöcken zu schaffen?

Mario Grid Wir können der obigen Logik folgen, indem wir die bisherigen Ideen kombinieren. Ändern Sie Ihren Code wie folgt:
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("#");
}
printf("\n");
}
}Beachten Sie, dass eine Schleife innerhalb einer anderen liegt – man sagt: es sind verschachtelte Schleifen. Die erste Schleife legt fest, welche vertikale Zeile gedruckt wird. Für jede Zeile werden drei Spalten gedruckt. Nach jeder Zeile wird eine neue Zeile gedruckt.
Was wäre, wenn wir sicherstellen wollten, dass die Anzahl der Blöcke konstant, d. h. unveränderlich ist? Ändern Sie Ihren Code wie folgt:
#include <stdio.h> int main(void) { const int n = 3; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("#"); } printf("\n"); } }Beachten Sie, dass “n” jetzt eine Konstante ist. Diese Variable kann nach ihrer Initialisierung nicht mehr geändert werden.
Häufiger sehen Sie in C dafür eine symbolische Konstante mit
#define. Sie steht oben in der Datei, unter den#include-Zeilen, ohne Gleichheitszeichen und ohne Semikolon:#define N 3Vor dem Übersetzen ersetzt der Präprozessor jedes
Nim Code durch3. Im Programm schreiben Sie danni < Nstatti < 3. Solche Namen schreibt man in Großbuchstaben.Mit dem, was wir schon wissen, können wir unseren Code so umbauen, dass der Benutzer nach der Größe des Rasters gefragt wird. Ändern Sie Ihren Code wie folgt:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n = get_int("Size: ");
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
printf("#");
}
printf("\n");
}
}- Beachten Sie, dass
get_intverwendet wird, um den Benutzer aufzufordern. - Ein allgemeiner Ratschlag bei der Programmierung lautet: Vertraue nie darauf, dass sich die Benutzerinnen und Benutzer so verhalten wie du denkst (oder wie man es erwarten würde). Sie werden sich wahrscheinlich falsch verhalten, entweder unabsichtlich oder absichtlich, und zum Beispiel ungültige Werte eingeben. Wir können unser Programm vor schlechtem Verhalten schützen, indem wir überprüfen, ob die Eingaben unseren Anforderungen genügen.
Was versteht man unter progressivem Refactoring?
Antwort
Mit einer einfachen, nicht optimalen Lösung beginnen und sie Schritt für Schritt verbessern: erst zum Laufen bringen, dann systematisch verbessern.
Wie nennt man es, wenn eine Schleife innerhalb einer anderen liegt?
Antwort
Verschachtelte Schleifen.
Warum ersetzt man „magische Zahlen“ im Code durch Variablen?
Antwort
Für bessere Lesbarkeit.
Was bewirkt const in const int n = 3;?
Antwort
n ist eine Konstante: Sie kann nach der Initialisierung nicht mehr geändert werden. Das verhindert ungewollte Änderungen.
Welcher allgemeine Ratschlag gilt beim Programmieren für Eingaben der Benutzer?
Antwort
Vertraue nie darauf, dass sich Benutzer so verhalten, wie du denkst. Deshalb prüft man Eingaben.
Übung: Mario in drei Stufen
Drei Aufgaben zum selben Programm. Wählen Sie die Stufe, die Sie schaffen; wer fertig ist, nimmt die nächste.
Stufe 1: Ein Quadrat, dessen Größe
ndas Programm mitget_inteinliest. Das ist das Programm oben. Tippen Sie es ab, falls Sie es noch nicht haben, und testen Sie es mit 3 und 5.Stufe 2: Ein Dreieck, linksbündig: In der ersten Zeile steht ein Stein, in der zweiten stehen zwei, in der letzten
n. Für die Größe 4:# ## ### ####Stufe 3: Dasselbe Dreieck rechtsbündig, mit Leerzeichen vor den Steinen:
# ## ### ####Das ist der Kern der Aufgabe Mario aus Übung 1; eine Lösung steht hier deshalb nicht. In der Übung legen Sie
mario.cin einem eigenen Ordnermarioan.
Lösung zu Stufe 2
Gegenüber dem Quadrat ändert sich eine einzige Stelle: Die innere Schleife läuft nicht mehr bis n, sondern bis zur Nummer der Zeile, j <= i. In der Zeile mit i gleich 0 gibt sie einen Stein aus, in der Zeile mit i gleich 1 zwei, und so weiter.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n = get_int("Size: ");
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= i; j++)
{
printf("#");
}
printf("\n");
}
}Eingabevalidierung: Das “cat”-Problem
Ein interessanter Aspekt von get_int: Wenn der Benutzer einen nicht-numerischen Wert eingibt (z.B. “cat” statt einer Zahl), fragt die Funktion automatisch erneut nach, bis eine gültige Zahl eingegeben wird. Aber: Wenn der Benutzer 0 eingibt, gibt get_int diese 0 zurück – und 0 ist keine gültige Größe für unser Mario-Grid!
Das bedeutet: Wir müssen zusätzlich überprüfen, ob die Zahl sinnvoll ist. Wir wollen sicherstellen, dass n mindestens 1 ist. Daher fragen wir erneut, solange n < 1 gilt.
Ändern Sie Ihren Code wie folgt:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n;
do
{
n = get_int("Size: ");
}
while (n < 1);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
printf("#");
}
printf("\n");
}
}- Beachten Sie, dass der Benutzer ständig zur Eingabe der Größe aufgefordert wird, bis die Eingabe des Benutzers 1 oder größer ist. Dazu haben wir eine do-while-Schleife verwendet, bei der der Schleifenkörper erst ausgeführt wird bevor die Bedingung geprüft wird.
int n;steht vor der Schleife, imdo-Block steht nur die Zuweisung. Legen Siendagegen im Block an (int n = get_int("Size: ");zwischen den geschweiften Klammern), gibt esnhinter der schließenden Klammer nicht mehr (Scope), undmakebricht beiwhile (n < 1)ab: „use of undeclared identifier ’n’“. Beachten Sie außerdem das Semikolon hinterwhile (n < 1).
Was tut get_int, wenn jemand „cat“ statt einer Zahl eingibt?
Antwort
Es fragt automatisch erneut nach, bis eine gültige Zahl eingegeben wird.
Warum muss man im Mario-Programm die Eingabe von get_int zusätzlich prüfen?
Antwort
get_int gibt auch 0 zurück, und 0 ist keine gültige Größe für das Raster.
Wann prüft eine do-while-Schleife ihre Bedingung?
Antwort
Erst nachdem der Schleifenkörper ausgeführt wurde. Der Körper läuft also mindestens einmal.
Kommentare
Kommentare sind grundlegende Bestandteile von Computerprogrammen. Damit hinterlassen Sie sich selbst und anderen, mit denen Sie zusammenarbeiten, Erklärungen zu Ihrem Code.
Code, den Sie in diesem Kurs erstellen und abgeben (insbesondere im Abschlussprojekt und in der Prüfung), muss immer sinnvolle und aussagekräftige Kommentare enthalten.
Normalerweise besteht ein Kommentar aus einigen Wörtern oder ein paar Sätzen, die dabei helfen, zu verstehen, was in einem bestimmten Code-Block passiert. Außerdem dienen solche Kommentare als Gedächtnisstütze für Sie, wenn Sie Ihren Code später überarbeiten müssen.
Kommentare stehen im Code immer hinter
//. Ändern Sie Ihren Code wie folgt, um Kommentare zu integrieren:#include <cs50.h> #include <stdio.h> int main(void) { // Prompt user for positive integer int n; do { n = get_int("Size: "); } while (n < 1); // Print an n-by-n grid of bricks for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("#"); } printf("\n"); } }Beachten Sie, dass jeder Kommentar mit einem
//beginnt.
Womit beginnt ein Kommentar in C?
Antwort
Mit //.
Wozu dienen Kommentare im Code?
Antwort
Sie erklären Ihnen selbst und anderen, was in einem Code-Block passiert, und sind eine Gedächtnisstütze, wenn Sie den Code später überarbeiten.
Fehlersuche
Jeder macht beim Programmieren Fehler.
Fehler in Programmen heißen „Bugs“ (Käfer). Das Wort war unter Ingenieuren schon lange üblich, als 1947 das Team um Grace Hopper in einem Relais des Rechners Mark II in Harvard eine echte Motte fand und ins Logbuch klebte, mit dem Vermerk „First actual case of bug being found“.
Betrachten Sie das folgende Bild aus der Aufgabe Mario:

mario Betrachten Sie außerdem den folgenden Code, in den absichtlich ein Fehler eingefügt wurde:
#include <stdio.h>
int main(void)
{
for (int i = 0; i <= 3; i++)
{
printf("#\n");
}
}Geben Sie
code buggy0.cin das Terminalfenster ein und schreiben Sie den obigen Code.Wenn Sie diesen Code ausführen, erscheinen vier Steine statt der geplanten drei.
Wir sehen uns nun vier Methoden zum Debugging an.
printfist eine sehr einfache und nützliche Methode, um Code zu debuggen. Wir könnten den Code dazu wie folgt abändern:#include <stdio.h> int main(void) { for (int i = 0; i <= 3; i++) { printf("i is %i\n", i); printf("#\n"); } }Wenn Sie diesen Code ausführen, werden Sie zahlreiche Ausgaben sehen, darunter
i is 0,i is 1,i is 2undi is 3. Wenn Sie das sehen, werden Sie vielleicht erkennen, dass der Code wie folgt korrigiert werden muss:#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("#\n"); } }Beachten Sie, dass das “<=” durch “<” ersetzt wurde.
Dieser Code kann wie folgt weiter verbessert werden:
#include <cs50.h> #include <stdio.h> void print_column(int height); int main(void) { int h = get_int("Height: "); print_column(h); } void print_column(int height) { for (int i = 0; i <= height; i++) { printf("#\n"); } }Beachten Sie, dass das Kompilieren und Ausführen dieses Codes immer noch zu einem Fehler führt.
Um diesen Fehler zu beheben, werden wir ein neues Werkzeug einsetzen, das uns zur Verfügung steht.
Dieses Werkzeug zur Fehlersuche ist der sogenannte Debugger, ein Softwaretool, das verwendet wird, um Fehler im Code aufzuspüren.
In VS Code wird Ihnen ein vorkonfigurierter Debugger zur Verfügung gestellt. Auf Rechnern mit ARM-Prozessor, etwa Macs mit Apple-Chip, läuft er im Dev Container nur, wenn der Container in der ARM-Fassung läuft. Einen Container, der vor dem 18. Oktober 2026 eingerichtet wurde, stellen Sie dafür einmal um (Dev Container auf ARM-Rechnern umstellen).
Um diesen Debugger zu verwenden, setzen Sie als erstes einen Breakpoint (Haltepunkt), indem Sie links neben eine Zeile Ihres Codes klicken – direkt links neben die Zeilennummer. Wenn Sie dort klicken, wird ein roter Punkt erscheinen. Stellen Sie sich dies als ein Stoppschild vor, das den Rechner auffordert, eine Pause einzulegen, damit Sie sich ansehen können, was in diesem Teil Ihres Codes passiert.

Haltepunkt Zweitens: Führen Sie
debug50 ./buggy0aus. Sie werden feststellen, dass, nachdem der Debugger aktiviert worden ist, eine Zeile Ihres Codes farbig markiert wird. Der Debugger hat den Rechner an dieser Codezeile angehalten. Beachten Sie in der oberen linken Ecke des Fensters, dass alle lokalen Variablen angezeigt werden, einschließlichi, das aktuell noch keinen Wert hat. Am oberen Rand des Fensters können Sie auf die mit einem Pfeil dargestellte Schaltflächestep overklicken (achten Sie auf den Tooltip beim Überfahren mit der Maus), und der Code wird weiter durchlaufen. Beachten Sie, wie sich der Wert voniändert.Dieses Werkzeug zeigt Ihnen zwar nicht, wo Ihr Fehler liegt, aber es hilft Ihnen, zu sehen, wie Ihr Code Schritt für Schritt abläuft. Sie können die Schaltfläche
step intobenutzen, um bei der Schritt-für-Schritt-Ausführung in eine Funktionen hineinzuschauen, anstatt diese mit step over zu überspringen.Eine weitere Form der Fehlersuche wird Rubber Duck Debugging genannt. Wenn Sie Probleme mit Ihrem Code haben, überlegen Sie sich, wie Sie mit einer Gummiente laut über das Codeproblem sprechen können. Wenn Sie lieber nicht mit einer kleinen Plastikente sprechen möchten, können Sie gerne mit einem Menschen in Ihrer Nähe sprechen! Diese Person muss nicht wissen, wie man programmiert: Es ist einfach so, dass man oft merkt, wo das Problem ist, wenn man seine Gedanken in Worte fasst. Probieren Sie es aus, Rubber Duck Debugging funktioniert wirklich!
Schließlich gibt es die Möglichkeit, sich von KI-Werkzeugen beim Debugging helfen zu lassen. Auf der Kursseite ist das Susi: Lassen Sie sich eine Fehlermeldung erklären oder einen Hinweis geben, und beheben Sie den Fehler dann selbst.
Die vier Methoden zur Fehlersuche aus der Vorlesung: printf, Debugger, ???, KI-Werkzeuge.
Antwort
Rubber Duck Debugging.
Warum gibt for (int i = 0; i <= 3; i++) vier statt der geplanten drei Steine aus?
Antwort
Wegen <= statt <: Die Schleife läuft für i gleich 0, 1, 2 und 3.
Was ist ein Debugger?
Antwort
Ein Softwaretool, um Fehler im Code aufzuspüren. Es zeigt, wie Ihr Code Schritt für Schritt abläuft.
Wozu setzt man im Debugger einen Breakpoint (Haltepunkt)?
Antwort
Damit der Rechner an dieser Zeile anhält und Sie sich ansehen können, was dort im Code passiert.
Mit welchem Befehl starten Sie in VS Code den Debugger für das Programm buggy0?
Antwort
debug50 ./buggy0
Was tut im Debugger step into bei einem Funktionsaufruf, anders als step over?
Antwort
Es geht in die Funktion hinein und führt sie Schritt für Schritt aus. step over überspringt sie.
Warum hilft Rubber Duck Debugging?
Antwort
Wenn man seine Gedanken in Worte fasst, merkt man oft selbst, wo das Problem ist.
Typen
- Eine der Schwächen von C ist, dass es sich von Haus aus nur in geringem Maß um die Speicherverwaltung kümmert. Zwar bietet C eine enorme Kontrolle über die Nutzung des Speichers, doch müssen Sie sich aller möglichen Fallstricke der Speicherverwaltung bewusst sein, um keine Fehler zu machen.
- Typen sind sehr wichtig, da jeder Typ bestimmte Grenzen hat und Regeln unterworfen ist. Zum Beispiel kann man in einem
intwegen der Grenzen des Speichers nur maximal 4294967296 verschiedene Werte ablegen (int-Werte belegen 32 Bit). - Die Anzahl der Bits begrenzt, wie hoch und niedrig wir zählen können.
- Wenn Sie aber versuchen, mit einem
inthöher als 2147483647 zu zählen, kommt es zu einem Integer Overflow, bei dem ein falscher Wert (typischerweise-2147483648) in der Variablen gespeichert wird. Derintist nämlich eine Ganzzahl mit Vorzeichen (signed) und eines der 32 Bit wird für das Vorzeichen benutzt. Daher können wir – anders als man erwarten würde – nur bis 231 – 1 zählen.
Zu den vier Typen vom Anfang (Variablen und Datentypen) kommen drei weitere:
long, Ganzzahlen mit mehr Bits (meist 64), die also viel höher zählen können als einint. Eingabe mitget_long, Ausgabe mit%li.float, eine Fließkommazahl, also eine Zahl mit Nachkommastellen. Eingabe mitget_float, Ausgabe mit%f.double, eine Fließkommazahl mit mehr Bits und damit mehr genauen Stellen als einfloat. Eingabe mitget_double, Ausgabe ebenfalls mit%f.
Auch feste Werte im Quelltext haben einen Typ. Solche Werte wie
42,3.14,'a'oder"Hallo"heißen Literale. Eine ganze Zahl ohne Punkt wie3ist einint, eine Zahl mit Punkt wie3.0eine Fließkommazahl. Deshalb ergibt4 / 3den Wert1und4 / 3.0den Wert1.333333(mehr bei der Ganzzahldivision).
Wie nennt man es, wenn ein int über 2147483647 hinaus zählen soll und dabei ein falscher Wert entsteht?
Antwort
Integer Overflow.
Warum kann ein int nur bis 2 hoch 31 minus 1 zählen, obwohl er 32 Bit hat?
Antwort
Er ist vorzeichenbehaftet (signed): Eines der 32 Bit wird für das Vorzeichen benutzt.
Was unterscheidet long von int?
Antwort
Ein long hat mehr Bits und kann also höher zählen.
Ganzzahldivision: Nachkommastellen werden abgeschnitten
Ersetzen Sie im Taschenrechner x + y durch x / y und geben Sie 1 und 3 ein, erhalten Sie 0 und nicht 0,333…. Teilt man in C eine Ganzzahl durch eine Ganzzahl, ist auch das Ergebnis eine Ganzzahl: C schneidet die Nachkommastellen ab (englisch truncation). Gerundet wird nicht. 7 / 2 ergibt 3, 2 / 3 ergibt 0 und -7 / 2 ergibt -3.
Es hilft auch nicht, das Ergebnis in einer float-Variablen zu speichern: float z = x / y; ergibt 0.000000, denn die Nachkommastellen sind schon bei der Division verloren. Sie müssen die Werte vorher in float umwandeln (englisch cast): (float) x / (float) y ergibt 0.333333.
Es genügt, dass ein Operand eine Fließkommazahl ist: Dann rechnet C mit Nachkommastellen. Auch (float) x / y ergibt 0.333333. Stehen die Zahlen fest im Quelltext, schreiben Sie eine davon mit Nachkommastelle: 4 / 3 ergibt 1, 4 / 3.0 ergibt 1.333333. Denn eine Zahl ohne Punkt wie 3 ist für C ein int, eine mit Punkt wie 3.0 eine Fließkommazahl.
Was gibt printf("%i\n", 7 / 2); aus?
Antwort
3. Bei der Division zweier Ganzzahlen schneidet C die Nachkommastellen ab (Truncation). Gerundet wird nicht.
x und y sind int-Variablen mit den Werten 1 und 3. Warum ergibt float z = x / y; trotzdem 0?
Antwort
x / y ist eine Ganzzahldivision: Die Nachkommastellen sind schon abgeschnitten, bevor das Ergebnis in z landet. Abhilfe: die Werte vorher umwandeln, (float) x / (float) y.
Was gibt printf("%f\n", 4 / 3); aus, und wie ändern Sie die Rechnung, damit 1.333333 herauskommt?
Antwort
4 / 3 teilt zwei int-Werte, das Ergebnis ist 1, und %f passt nicht zu einem int: make bricht ab. Es genügt, einen Operanden als Fließkommazahl zu schreiben: 4 / 3.0 (oder 4.0 / 3) ergibt 1.333333.
Fließkomma-Ungenauigkeit: Die Messbecher-Analogie
Ein besonderes Problem bei der Programmierung betrifft Fließkommazahlen (float und double). Diese können manche Zahlen nicht präzise darstellen. Ein gutes Beispiel ist der Bruch 1/3:
Stellen Sie sich einen Messbecher vor: Er hat Markierungen für 1/4, 1/2 und 3/4. Aber wo ist die Markierung für 1/3? Sie existiert nicht! Wenn Sie 1/3 Liter abmessen sollen, müssen Sie schätzen – irgendwo zwischen der 1/4- und 1/2-Markierung.
Genauso funktionieren float-Variablen: Sie haben eine begrenzte Präzision. Sie können viele Zahlen darstellen, aber nicht alle exakt. Ein float ist wie ein Messbecher mit wenigen Markierungen, ein double hat mehr Markierungen (also bessere Genauigkeit), aber auch ein double ist nicht perfekt.
Daher sehen Sie manchmal statt 0,333333… plötzlich 0,33333334 – der Computer musste schätzen, weil die exakte Darstellung von 1/3 mit endlich vielen Bits unmöglich ist.
Deshalb vergleichen Sie Fließkommazahlen nicht mit ==. Schon 0.1 + 0.2 == 0.3 ist in C falsch, weil keine der drei Zahlen exakt dargestellt wird.
Praktischer Tipp: Verwenden Sie niemals float oder double für Geldbeträge! Nutzen Sie stattdessen Ganzzahlen (z.B. Cent-Beträge als int), um Rundungsfehler zu vermeiden.
- Achten Sie beim Programmieren besonders auf die Arten von Variablen, die Sie verwenden, um Probleme in Ihrem Code zu vermeiden.
- Wir haben uns in der Vorlesung einige Beispiele für Katastrophen angesehen, die durch speicherbezogene Fehler entstehen können (Jahr-2000-Problem, Jahr-2038-Problem, Pac Man Level 256, Donkey Kong Level 22, Boeing 787).
Warum zeigt ein float für 1/3 nicht exakt 0,333… an?
Antwort
Fließkommazahlen haben eine begrenzte Präzision: 1/3 lässt sich mit endlich vielen Bits nicht exakt darstellen, der Computer muss schätzen.
Welchen Typ sollten Sie für Geldbeträge verwenden?
Antwort
Ganzzahlen, etwa Cent-Beträge als int, niemals float oder double. So vermeiden Sie Rundungsfehler.
Was davon zur Basis gehört
Die Shorts zu diesem Abschnitt sagen auf ihren Seiten, welche Folien wozu gehören.
Basis-Code (Programmieren können):
- Variablen, Datentypen, Operatoren, Bedingungen, Schleifen, Ausgabe mit printf (Formatcodes, Escape-Sequenzen)
- Funktionen, Parameter, Rückgabewerte, Scope
- Integer Overflow (erkennen und umgehen)
- Ganzzahldivision umgehen (ein Operand als Fließkommazahl: Cast oder Literal wie 3.0), Float-Ungenauigkeit umgehen (größerer Typ; Geldbeträge in Cent als int)
- Kommentare, Codestil (Einrückung, Klammersetzung, sprechende Namen)
- Eingabevalidierung (Schleife bis zur gültigen Eingabe)
- Symbolische Konstante mit #define anlegen
- Einfache Shell-Befehle (cd, ls, cp, mv, mkdir, rm, cat, pwd)
Basis-Konzept (Erklären und von Hand durchspielen):
- switch lesen, auch das Durchfallen ohne break
- Compiler-Fehlermeldungen lesen; Syntax-, Logik- und Designfehler unterscheiden
- Magic Numbers erkennen
Kür-Code (Anwenden und schreiben):
- const-Variablen
- Progressives Refactoring
- Pipes, Umleitungen, Wildcards, Textfilter (sort, uniq, cut), komplexere Shell-Konstruktionen
Hintergrund (Einordnung):
- Manual Pages (man)
Was die Labels bedeuten und die ganze Liste: Basis und Kür.
Zusammenfassung
In dieser Vorlesung haben Sie gesehen, wie Sie die Bausteine, die Sie in Scratch kennengelernt haben, auf die Programmiersprache C anwenden können. Sie haben gelernt:
- wie Sie Funktionen aus Bibliotheken benutzen und Ihre eigenen Funktionen schreiben;
- wie Sie Variablen, Bedingte Anweisungen und Schleifen verwenden;
- wie man Abstraktion einsetzt, um seinen Code zu vereinfachen und zu verbessern;
- wie man bei der Lösung eines Informatikproblems vorgeht;
- wie man die Linux-Kommandozeile benutzt;
- wie man Kommentare in seinen Code einbaut;
- wie man Typen und Operatoren verwendet.
Außerdem haben Sie gesehen, wie Sie Fehler finden: erklären (Rubber Duck), mit printf nachsehen und mit dem Debugger anhalten.
Dies war Inf-Einf-B. Bis zum nächsten Mal!
Die Fragen zum Selbsttest helfen beim aktiven Lesen. Sie sind kein Vorgeschmack auf die Prüfung: Fragen und Antworten auswendig zu lernen reicht nicht zum Bestehen und ist vermutlich auch keine sinnvolle Vorbereitung. Was hilft: Wie Sie Inf-Einf-B bewältigen.
Diese Seite als Markdown: ansehen herunterladen