Zeit zum Durchschnaufen (92 Abgaben in VC; 115 letzte Woche).
(1) Wir wiederholen C-Konzepte. (2) Dann beantworten Sie 20 Fragen.
Nach der Pause bearbeiten wir (3) eine Live-Coding-Aufgabe.
Später kreuzen Sie auf einem Zettel an, welche Aufgaben sie lösen konnten. Die Zettel sammeln wir am Ende anonym ein. Reißen Sie das Pseudonym ab oder machen Sie ein Foto. Nach dieser Vorlesung werten wir Ihre bisherigen Kreuze aus. Dann können wir Ihren Lernfortschritt auswerten, wenn Sie uns Ihre Pseudonyme mitteilen. Details folgen im Lauf der Woche.
continue und breakZwei wichtige Befehle zur Kontrolle von Schleifen:
continue - Springe zum nächsten Durchlaufbreak - Beende die Schleife sofort↓ Details mit Beispielen
continue - Nächster DurchlaufÜberspringt den Rest des aktuellen Durchlaufs und macht mit dem nächsten weiter.
for (int i = 0; i < 5; i++)
{
if (i == 2)
{
continue; // überspringt Rest des Durchlaufs
}
printf("%i ", i);
}
Ausgabe: 0 1 3 4
break - Schleife beendenBeendet die Schleife sofort und komplett.
for (int i = 0; i < 5; i++)
{
if (i == 2)
{
break; // Bricht Schleife ab
}
printf("%i ", i);
}
Ausgabe: 0 1
%Was ist Modulo?
Der Modulo-Operator (%) gibt den Rest einer Division zurück.
17 % 5 = 2 // 17 ÷ 5 = 3 Rest 2
23 % 10 = 3 // 23 ÷ 10 = 2 Rest 3
10 % 2 = 0 // 10 ÷ 2 = 5 Rest 0
↓ Anwendungsbeispiele
int n = 17;
if (n % 2 == 0)
{
printf("gerade\n");
}
else
{
printf("ungerade\n");
}
Ausgabe: ungerade
Wenn n % 2 gleich 0 ist, ist die Zahl gerade!
int number = 1234;
int last_digit = number % 10;
Ergebnis: last_digit = 4
number % 10 gibt immer die letzte Ziffer zurück!
int cents = 67;
int quarters = cents / 25;
int remaining = cents % 25;
Ergebnis: quarters = 2, remaining = 17
int x = 1;
int y = 3;
printf("%f\n", x / y); // Ausgabe: 0.000000 – falsch
Warum? Beide Operanden sind int → Integer-Division!
↓ Lösungen
float x = 1.0;
float y = 3.0;
printf("%f\n", x / y);
Ausgabe: 0.333333
int x = 1;
int y = 3;
printf("%f\n", (float) x / y);
Ausgabe: 0.333333
Mindestens ein Operand muss float sein!
int x = 1;
printf("%f\n", x / 3.0);
Ausgabe: 0.333333
3.0 ist ein float → Float-Division!
int a = 3, b = 3, c = 4;
float avg = (a + b + c) / 3; // 3.000000 (falsch!)
int a = 3, b = 3, c = 4;
float avg = (a + b + c) / 3.0; // 3.333333 (richtig)
Ein Array mit Größe n hat Indizes von 0 bis n-1
Bei arr[n] ist n-1 der letzte gültige Index!
Verwende i < n, nicht i <= n
↓ Beispiele
int arr[3]; // Größe 3
arr[1] = 10;
arr[2] = 20;
arr[3] = 30;
Problem: arr[3] existiert nicht!
Array mit Größe 3 → Indizes 0, 1, 2
int arr[3];
for (int i = 0; i <= 3; i++) // ← <= ist falsch!
{
arr[i] = i; // arr[3] → Fehler!
}
int arr[3];
for (int i = 0; i < 3; i++) // ← < ist richtig!
{
arr[i] = i;
// Nur 0, 1, 2
}
\0Was ist ein String?
Ein String ist ein char-Array mit einem speziellen Endzeichen: \0
↓ Beispiel
string s = "Hi!";
Im Speicher:
[H] [i] [!] [\0]
0 1 2 3
4 Bytes im Speicher: 3 sichtbare Zeichen + 1 Terminator
String = char-Array + \0 am Ende
strlen#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = "Hello";
int n = strlen(s);
printf("Länge: %i\n", n);
}
Ausgabe: Länge: 5
Wichtig:
strlen() gibt die Anzahl der Zeichen zurück (ohne \0)\0)toupper und tolowerFunktionen aus <ctype.h> zum Umwandeln von Zeichen
↓ Beispiele
toupper und tolower - Einzelne Zeichen#include <ctype.h>
#include <stdio.h>
int main(void)
{
char c = 'a';
printf("%c\n", toupper(c));
char d = 'Z';
printf("%c\n", tolower(d));
}
Ausgabe: A und z
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
string word = "hello";
for (int i = 0; i < strlen(word); i++)
{
printf("%c", toupper(word[i]));
}
printf("\n");
}
Ausgabe: HELLO
Was sind Exit-Codes?
Jedes Programm gibt beim Beenden einen Wert zurück: den Exit-Code
↓ Beispiele
return 0 - Erfolgint main(void)
{
printf("Hello!\n");
return 0; // Alles OK
}
return 0 bedeutet: Programm erfolgreich beendet!
return 1 - Fehlerint main(void)
{
printf("Error!\n");
return 1; // Fehler
}
return 1 (oder andere Werte ≠ 0) bedeutet: Fehler aufgetreten!
$ ./programm
$ echo $?
0
Der Befehl echo $? zeigt den Exit-Code des letzten Programms
return 0 = Erfolg
return 1 = Fehler
↓ Float-Formatierung und Prozent-Zeichen
float x = 3.14159265;
printf("%f\n", x);
printf("%.2f\n", x);
printf("%.0f\n", x);
Ausgaben: 3.141593 / 3.14 / 3
%.2f → 2 Dezimalstellen, %.0f → keine Dezimalstellen
float avg = 0.75;
// FALSCH:
printf("%.0f%\n", avg * 100); // Fehler! % ist reserviert
// RICHTIG:
printf("%.0f%%\n", avg * 100); // 75%
Ausgabe: 75%
%% wird benötigt, da % ein Formatzeichen ist!
Bearbeiten Sie die folgenden 20 Aufgaben alleine (ca. 120 Sekunden pro Aufgabe) auf Ihrem Zettel. Notieren Sie sich nach der Auflösung, ob Sie richtig lagen. Machen Sie dann ein Kreuz (☑︎). Die Zettel sammeln wir am Ende anonym ein.
Reißen Sie sich das Pseudonym ab oder machen Sie ein Foto.
Lösen Sie zusätzlich die Bonus-Fragen, falls Sie Zeit übrig haben (irrelevant für das Korrekt-Kreuz).
Ziel: Feedback zu eigenen Fähigkeiten: "Was weiß ich, was muss ich mir noch einmal ansehen?" Zusätzlich: Rückmeldung für uns ("Was wissen sie?")
Navigation mit Pfeiltasten (links/rechts, zur Lösung: nach unten, Esc für Übersicht).
Was gibt dieser Code aus?
// hier stehen alle benötigten includes
string s = "Inf-Einf-B!";
int n = strlen(s);
for (int i = 0; i < n; i++)
{
printf("%c", toupper(s[i]));
}
printf("\n");
Der Code gibt INF-EINF-B! aus.
strlen(s) gibt String-Length zurück, die Schleife läuft über alle Zeichen, toupper() wandelt jeden Buchstaben in Großbuchstaben um.
Wie oft wird "Hi" ausgegeben?
int i = 0;
do {
printf("Hi\n");
i++;
} while (i < 0);
printf("Bye\n");
Bonus: Was würde sich ändern, wenn man statt do-while eine while-Schleife verwendet?
"Hi" wird einmal ausgegeben, da do-while immer mindestens einmal ausführt.
Bonus: Bei einer while-Schleife würde nichts ausgegeben, da die Bedingung von Anfang an false ist.
Schreiben Sie Code, der folgendes prüft: Enthält die int-Variable x eine Zahl zwischen 1 und 10 (inklusive)? Dann "Gültig" ausgeben, sonst nichts. Auf #includes und die main-Funktion können Sie verzichten.
Fertig? Bitte 3x auf den Tisch klopfen.
if (x >= 1 && x <= 10)
{
printf("Gültig\n");
}
Wie viele Syntaxfehler enthält dieser Code?
#include <stdio.h>
int main(void)
{
int x = get_int("x: ")
if (x == 5)
{
printf("x ist fünf")
}
return 0
}
Der Code enthält 4 Syntaxfehler:
#include <cs50.h>get_int("x: ")printf("x ist fünf")return 0Was ist das Problem mit diesem Code?
// hier stehen alle benötigten includes
// in irgendeiner Funktion
string s = "MeinText";
int uppercase = 0;
for (int i = 0; i < strlen(s); i++)
{
if (s[i] >= 'A' && s[i] <= 'Z')
{
uppercase++;
}
}
float anteil = uppercase / strlen(s);
printf("Uppercase: %f%%\n", anteil * 100.0);
Problem: Integer-Division!
Der Text "MeinText" hat 2 Großbuchstaben (M, T) von 8 Zeichen = 25%
Aber: 2 / 8 = 0 (Integer-Division), dann 0 * 100 = 0
Das Programm gibt 0% aus statt 25%!
Lösung: float anteil = (float) uppercase / strlen(s);
Schreiben Sie eine Schleife ohne die Modulo-Operation zu verwenden, die die ersten 100 geraden Zahlen ausgibt, also 2, 4, 6, ... 200.
Bonus: Schreiben Sie eine Alternative mit Modulo.
for (int i = 1; i <= 100; i++)
{
printf("%i\n", i * 2);
}
for (int i = 1; i <= 200; i++)
{
if (i % 2 == 0)
{
printf("%i\n", i);
}
}
Schreiben Sie Code, der die Länge eines Strings ohne strlen() berechnet.
string s = get_string("Text: ");
int n = 0;
while (s[n] != '\0')
{
n++;
}
printf("Länge: %i\n", n);
Die Schleife läuft durch den String bis zum \0-Terminator und zählt die Zeichen.
Wie viele Syntaxfehler? Welcher Exit-Code bei Eingabe von 10?
int main(void)
{
int num = get_int("Number: ");
printf("You entered %i\n", num)
if (num % 2)
{
return 1;
}
return 0;
}
1 Syntaxfehler: Fehlendes Semikolon nach printf
Exit-Code bei 10: 0
Erklärung:
num % 2 bei einer geraden Zahl (10) ergibt 0if (0) ist false → der if-Block wird nicht ausgeführtreturn 0; wird ausgeführt → Exit-Code ist 0Was gibt folgender Code aus?
const int N = 6;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (i == j) {
printf(" ");
continue;
}
printf("# ");
}
printf("\n");
}
Bonus: Was wäre bei break statt continue?
continue bricht Durchlauf ab, setzt beim nächsten fort:
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
Bonus: Nur das untere Dreieck wird ausgegeben:
#
# #
# # #
# # # #
# # # # #
Details: s. Short zu Schleifen
Welchen Exit-Code gibt dieses Programm zurück, wenn es ohne Argumente mit ./programm aufgerufen wird?
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./programm <name>\n");
return 1;
}
printf("Hello, %s\n", argv[1]);
return 0;
}
Bonus: Was wäre der Exit-Code bei ./programm Alice?
Exit-Code: 1 (Fehler)
Erklärung:
./programm ist argc = 1 (nur Programmname)argc != 2 ist true → if-Block wird ausgeführtreturn 1; signalisiert einen FehlerBonus: Bei ./programm Alice wäre der Exit-Code 0 (Erfolg), da argc = 2
Schreiben Sie eine for-Schleife, die jede zweite Zahl des Arrays ausgibt (also 4, 7, 3).
Array: int zahlen[] = {4, 2, 7, 1, 3};
Bonus: Wie würden Sie die Zahlen in umgekehrter Reihenfolge ausgeben?
for (int i = 0; i < 5; i += 2) {
printf("%i ", zahlen[i]);
}
// Alternative mit Modulo
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
printf("%i ", zahlen[i]);
}
}
Ausgabe beider Lösungen: 4 7 3 (Indizes 0, 2, 4)
Bonus: Um die Zahlen in umgekehrter Reihenfolge auszugeben:
for (int i = 4; i >= 0; i--) {
printf("%i ", zahlen[i]);
}
Sie haben versehentlich eine Datei namens "Hello.c" (mit großem H) erstellt. Welche Befehle führen Sie nacheinander aus um:
Bonus: Wie können Sie sich den Inhalt der Datei anzeigen lassen?
$ ls
$ mv Hello.c hello.c
$ ls
Bonus:cat hello.c
In der Datei names.txt steht:
1: Alice Smith
2: Bob Smith
3: Alice Brown
Welche Variante ermittelt, welcher Nachname am häufigsten vorkommt?
cat names.txt | cut -d' ' -f3 | sort | uniq -c | sort -n
cat names.txt | cut -d':' -f2 | cut -d' ' -f3 | sort | uniq -c
cat names.txt | cut -d' ' -f3 | sort | uniq -c | sort -n
Details: siehe Short zu Kommandozeile.
Welches Problem bzw. welche Probleme hat das Programm?
// benötigte includes sind vorhanden
int main(void)
{
float sum = 0;
const float TICKET_PRICE = 0.8;
for (int i = 0; i < 8; i++) {
sum += 0.10;
printf("Eingeworfen: %.2f €\n", sum);
if (sum == TICKET_PRICE) {
printf("Danke! Das Ticket wird gedruckt.\n");
} else {
printf("Bitte werfen Sie 0.80 EUR ein!\n");
}
}
}
Problem: Float-Vergleich mit ==
Eingeworfen werden 8× 10 Cent; Ticket sollte gedruckt werden.
Aber: sum wird nie exakt 0.8, sondern z.B. 0.8000000715
→ sum == TICKET_PRICE ist immer false!
Besser mit int-Cent-Beträgen arbeiten:
int sum = 0;
const int TICKET_PRICE = 80; // 80 Cent
for (int i = 0; i < 8; i++) {
sum += 10; // 10 Cent Münze
printf("Eingeworfen: %i Cent\n", sum);
if (sum == TICKET_PRICE) {
printf("Danke! Das Ticket wird gedruckt.\n");
} else {
printf("Bitte werfen Sie mehr Münzen ein!\n");
}
}
Überarbeiten Sie dieses fehlerhafte Programm, sodass es ausführbar ist und die gewünschte Funktionalität hat:
// benötigte includes sind vorhanden
int main(int argc, string argv[])
{
for (int i = 0; i < argc; i++)
{
printf("%i ", i);
if(i == 3) { break; }
}
printf("\nLetzter Durchlauf war: %i\n", i);
}
Problem: i ist nur in der for-Schleife gültig (Scope) → Compiler-Fehler
Lösung: i vor der Schleife deklarieren:
// benötigte includes sind vorhanden
int main(int argc, string argv[])
{
int i;
for (i = 0; i < argc; i++)
{
printf("%i ", i);
if(i == 3) { break; }
}
printf("\nLetzter Durchlauf war: %i\n", i);
}
Was genau (genauen Wert und allgemein) gibt der Code aus?
string text = "Hello, World!";
int x = 0;
for (int i = 0; i < strlen(text); i++)
{
if ((text[i] >= 'a' && text[i] <= 'z') ||
(text[i] >= 'A' && text[i] <= 'Z'))
{
x++;
}
}
printf("x: %i\n", x);
Ausgabe: x: 10
Der Code zählt nur Buchstaben (a-z, A-Z), keine Sonderzeichen.
"Hello, World!" hat 10 Buchstaben: H, e, l, l, o, W, o, r, l, d
Komma, Leerzeichen und Ausrufezeichen werden nicht gezählt.
Welchen Exit-Code gibt dieser Code zurück?
int mystery(int x)
{
if (x <= 0)
return 0;
return x + mystery(x - 1);
}
int main(void)
{
return mystery(3);
}
Bonus: Mit welchem arithmetischen Ausdruck könnte man den Code in mystery ersetzen, um das Ergebnis effizienter zu berechnen?
Der Code gibt den Wert 6 zurück (Summe von 1 bis x).
Bonus: return x * (x – 1) / 2
Was macht dieser Code?
char c = 'a';
while (c <= 'z')
{
printf("%c: %i %i\n", c, c, c - 'a');
c++;
}
Der Code gibt alle Kleinbuchstaben von 'a' bis 'z' zusammen mit ihren ASCII-Werten aus gefolgt von den Ziffern 0 bis 25.
Ausgabe:
a: 97 0
b: 98 1
...
z: 122 25
Schreiben Sie Code, der die ersten beiden Zeichen eines Strings s (Länge: 10) vertauscht.
Beispiel: Aus "CS" wird "SC".
char temp = s[0];
s[0] = s[1];
s[1] = temp;
Dieser Code tauscht die ersten beiden Zeichen des Strings s.
Ordnen Sie die Zeilen so an, dass das Programm das Maximum im Array findet. Achtung: Nicht alle Zeilen werden benötigt.
if (scores[i] >= max) // A
{ // B
if (scores[i] == max) // C
} // D
{ // E
if (scores[i] < max) // F
int max = scores[0]; // G
max = scores[i]; // H
printf("Maximum: %i\n", max); // I
} // J
if (scores[i] > max) // K
int scores[] = {72, 85, 91, 68, 77}; // L
for (int i = 1; i < 5; i++) // M
int scores[] = {72, 85, 91, 68, 77}; // L
int max = scores[0]; // G
for (int i = 1; i < 5; i++) // M
{ // B
if (scores[i] > max) // K
{ // E
max = scores[i]; // H
} // D
} // J
printf("Maximum: %i\n", max); // I
Der Code findet: Maximum: 91
if (scores[i] < max) - sucht Minimum, nicht Maximumif (scores[i] >= max) - überschreibt bei Gleichheit (unnötig)if (scores[i] == max) - findet nur gleiche Werte, nicht größereDie Schleife startet bei i = 1 (nicht 0), weil wir max bereits mit scores[0] initialisiert haben.
Bitte zählen Sie nun, wie viele Aufgaben Sie korrekt gelöst haben.
Geben Sie dann die Zettel nach außen; wir möchten die Zettel gerne anonym statistisch auswerten.