Warm-ups (freiwillig)
Sie müssen nicht alle Warm-ups machen. Suchen Sie sich in der Tabelle das heraus, was Ihnen für eine Aufgabe fehlt:
| Übt | Hilft bei | |
|---|---|---|
| Warm-up 1 | Adressen ausgeben mit & | Vorlesung 4 (Pointer) |
| Warm-up 2 | Pointer: über eine Adresse lesen und schreiben | Vorlesung 4 (Pointer) |
| Warm-up 3 | Ein Bild als zweidimensionales Array von Structs: Pixel lesen und ändern | Filter (grayscale, sepia, reflect) |
| Warm-up 4 | Nachbarpixel finden, ohne über den Bildrand zu greifen | Filter (blur) |
Legen Sie für die Warm-ups eine eigene Datei an, zum Beispiel warmup_aufgaben_1.c für Übung 1, im selben Ordner wie Ihre Übungsaufgaben. Die meisten Warm-ups bauen aufeinander auf: Sie ergänzen die Datei Schritt für Schritt. Lösungen gibt es für die Warm-ups nicht. Unter „Wo zu finden?“ steht jeweils, wo das Thema in Vorlesung und Shorts vorkommt. Mehr dazu im FAQ zu den Warm-ups.
Warm-up 1: Adressen ausgeben mit &
Kopieren Sie das folgende Code-Snippet und ersetzen Sie den Kommentar, sodass die Adresse von buffer ausgegeben wird. Erinnern Sie sich, dass Sie mit & auf die Adresse einer Variablen zugreifen können.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
char buffer;
printf("An der Adresse %p liegt der Inhalt '%c'.\n", /* Adresse des Buffers */, buffer);
return 0;
}Führen Sie das Programm mehrfach aus. Was fällt Ihnen auf?
Wo zu finden?
Bei Problemen und Unklarheiten schauen Sie sich am besten noch einmal die Notizen zu den Programmen addresses.c und garbage.c aus der Vorlesung 4. Memory sowie das Short zu Pointern an.
Warm-up 2: Pointer: über eine Adresse lesen und schreiben
Fügen Sie zwischen printf und return folgenden Code ein:
char *pointer = &buffer;
*pointer = 'A';
printf("An der Adresse %p (Ausgabe `pointer`) liegt der Inhalt '%c' (Ausgabe `buffer`) / '%c' (Ausgabe `*pointer`).\n", pointer, buffer, *pointer);
buffer = 'B';
printf("An der Adresse %p (Ausgabe `pointer`) liegt der Inhalt '%c' (Ausgabe `buffer`) / '%c' (Ausgabe `*pointer`).\n", pointer, buffer, *pointer);Führen Sie das Programm aus und vollziehen Sie die Ausgabe nach. Was passiert jeweils bei den Zuweisungen?
Erinnerung: Ein Pointer ist letztlich nur eine Variable, deren Inhalt eine Adresse ist. Der Typ des Pointers bezieht sich dabei auf den Inhalt, der an dieser Adresse zu finden ist. Dadurch weiß der Compiler beim Dereferenzieren, wie viele Bytes ab dieser Adresse gelesen werden müssen, um einen Wert des entsprechenden Typs zu erhalten. Bei einem char-Pointer wäre dies z. B. 1 Byte (8 Bit), da ein char in C 1 Byte groß ist.
Visualisieren Sie sich dies am besten einmal mit einer Handzeichnung
- Welche Werte stehen an der jeweiligen Stelle?
- Worauf kann mit welcher Syntax zugegriffen werden? Ordnen Sie
buffer,&buffer,pointer,&pointerund*pointerden entsprechenden Boxen zu. - Nutzen Sie Pfeile, um Verbindungen zwischen den Inhalten zu visualisieren.
- Vollziehen Sie dann den Code (d. h. die Zuweisungen und die daraus resultierenden Veränderungen) anhand Ihrer Zeichnung nach.

Wo zu finden?
Warm-up 3: Ein Bild als Tabelle von Pixeln
In Filter liegt das Bild als zweidimensionales Array image vor: image[i] ist die Zeile i, image[i][j] der Pixel in Zeile i und Spalte j. Jeder Pixel ist ein RGBTRIPLE, ein struct mit drei Farbwerten zwischen 0 und 255. Den Zugriff darauf üben Sie hier an einem kleinen Bild mit 3 Zeilen und 4 Spalten, ganz ohne BMP-Datei.
Legen Sie für die Warm-ups 3 und 4 eine neue Datei warmup_bild.c an und kopieren Sie dieses Gerüst hinein. Den Typ RGBTRIPLE haben wir aus der Datei bmp.h im Filter-Gerüst übernommen:
#include <stdint.h>
#include <stdio.h>
// wie in bmp.h aus dem Filter-Gerüst
typedef uint8_t BYTE;
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} RGBTRIPLE;
int main(void)
{
// 3 Zeilen (height), 4 Spalten (width)
// Reihenfolge in den inneren Klammern wie im struct: Blau, Grün, Rot
RGBTRIPLE image[3][4] = {
{{0, 0, 255}, {0, 255, 0}, {255, 0, 0}, {255, 255, 255}}, // rot, grün, blau, weiß
{{0, 0, 0}, {128, 128, 128}, {0, 255, 255}, {0, 165, 255}}, // schwarz, grau, gelb, orange
{{0, 0, 139}, {208, 224, 64}, {128, 0, 128}, {203, 192, 255}} // dunkelrot, türkis, lila, rosa
};
return 0;
}Geben Sie zuerst die drei Farbwerte des Pixels image[1][2] aus:
$ ./warmup_bild
Rot: 255, Grün: 255, Blau: 0Welcher Pixel aus den Kommentaren ist das? Und warum steht er im Code als {0, 255, 255}?
Schreiben Sie dann eine Funktion, die die Rotwerte aller Pixel als Tabelle ausgibt, eine Bildzeile pro Textzeile. Verwenden Sie dieselbe Parameterliste wie die Funktionen in helpers.c von Filter:
void zeige_rot(int height, int width, RGBTRIPLE image[height][width])Mit dem Aufruf zeige_rot(3, 4, image); in main erscheint nach der Zeile mit den Farbwerten:
255 0 0 255
0 128 255 255
139 64 128 255Schreiben Sie zum Schluss eine zweite Funktion abdunkeln mit derselben Parameterliste, die bei jedem Pixel alle drei Farbwerte halbiert. Ganzzahlige Division reicht, Nachkommastellen fallen weg. Rufen Sie in main nacheinander zeige_rot, abdunkeln und noch einmal zeige_rot auf. Die zweite Tabelle sieht so aus:
127 0 0 127
0 64 127 127
69 32 64 127abdunkeln gibt nichts zurück. Warum sieht main die geänderten Werte trotzdem?
Tipp
- Über ein zweidimensionales Array laufen Sie mit zwei verschachtelten Schleifen: außen über die Zeilen (
ivon 0 bisheight - 1), innen über die Spalten (jvon 0 biswidth - 1). - Der Zeilenumbruch gehört in die äußere Schleife, hinter die innere.
%4igibt eine Zahl rechtsbündig mit vier Zeichen Breite aus. So stehen die Spalten untereinander.- Stehen Ihre Funktionen unterhalb von
main, braucht der Compiler oberhalb vonmainihre Prototypen, so wie inhelpers.h.
Wo zu finden?
Zweidimensionale Arrays stehen am Anfang der Notes zur Vorlesung 4, structs und die Punktschreibweise in den Notes zur Vorlesung 3 und im Short zu eigenen Datentypen. Wie man ein Array an eine Funktion übergibt, zeigt die Funktion average in den Notes zur Vorlesung 2. Zur Frage am Ende hilft der Abschnitt Swap.
Warm-up 4: Nachbarpixel finden, ohne über den Bildrand zu greifen
Für blur brauchen Sie zu jedem Pixel alle Pixel in dem 3×3-Kasten um ihn herum. In der Mitte des Bildes sind das 9, am Rand weniger: Eine Zeile -1 oder eine Spalte width gibt es nicht. C bemerkt es aber nicht, wenn Sie trotzdem image[-1][0] lesen. Sie bekommen dann Werte, die zufällig an dieser Stelle im Speicher liegen. Deshalb müssen Sie vor jedem Zugriff selbst prüfen, ob Zeile und Spalte im Bild liegen.
Schreiben Sie eine Funktion void zaehle_nachbarn(int height, int width), die für jeden Pixel ausgibt, wie viele Pixel seines 3×3-Kastens im Bild liegen; der Pixel selbst zählt mit. Für das Bild aus Warm-up 3 ergibt der Aufruf zaehle_nachbarn(3, 4);:
4 6 6 4
6 9 9 6
4 6 6 4Prüfen Sie mit zaehle_nachbarn(5, 6);, ob Ihre Funktion auch für andere Bildgrößen stimmt:
4 6 6 6 6 4
6 9 9 9 9 6
6 9 9 9 9 6
6 9 9 9 9 6
4 6 6 6 6 4Die Funktion braucht nur die Maße des Bildes, nicht die Pixel selbst. Bei blur lesen Sie an dieser Stelle zusätzlich die Farbwerte der Nachbarn. Warum Sie dort außerdem eine Kopie des Bildes brauchen, steht in der Hilfestellung zu blur in der Aufgabe Filter.
Tipp
- Zu den beiden Schleifen über Zeilen und Spalten kommen zwei weitere für den Kasten: Zeile
i - 1bisi + 1, Spaltej - 1bisj + 1. - Eine Zeile
zeileliegt im Bild, wennzeile >= 0undzeile < heightgilt. Für Spalten gilt dasselbe mitwidth. Alle vier Bedingungen verbinden Sie mit&&. - Der Zähler muss für jeden Pixel wieder bei 0 anfangen.
Wo zu finden?
Was passiert, wenn man über die Grenzen eines Arrays hinaus liest, steht im Abschnitt Arrays und Speichergrenzen der Notes zur Vorlesung 2. Das Raster mit dem 3×3-Kasten ist in der Aufgabe Filter bei blur abgebildet.
Diese Seite als Markdown: ansehen herunterladen