MINT lernen

Textaufgaben: Speicher und Adressen

Eine Sensorbox zeigt, wo ihre Messwerte im Speicher liegen, und ein Vokabeltrainer stürzt ab, weil scanf eine Adresse statt eines Werts braucht.

Dein Fortschritt:
0 / 0 Aufgaben
1

Die Sensorbox im Klassenraum

AFB I–II

Die Informatik-AG baut eine Sensorbox, die im Raum B die Temperatur und den CO2-Gehalt der Luft misst. Luca möchte wissen, wie die Messwerte im Arbeitsspeicher liegen. Er schreibt ein kleines Testprogramm (%zu ist die passende Formatangabe für Werte von sizeof) und startet es zweimal hintereinander.

C · sensorbox.c
#include <stdio.h>

int main(void) {
    char raum = 'B';
    int co2 = 812;           // CO2-Gehalt in ppm
    double temp = 21.5;      // Temperatur in °C

    printf("Groessen: %zu %zu %zu\n", sizeof(raum), sizeof(co2), sizeof(temp));
    printf("raum: %p\n", &raum);
    printf("co2:  %p\n", &co2);
    printf("temp: %p\n", &temp);
    return 0;
}
Konsole · zwei Programmstarts
$ ./sensorbox
Groessen: 1 4 8
raum: 0x7fff2078549b
co2:  0x7fff2078549c
temp: 0x7fff207854a0
$ ./sensorbox
Groessen: 1 4 8
raum: 0x7fffc795752b
co2:  0x7fffc795752c
temp: 0x7fffc7957530

Für den Unterricht hat Luca die Lage der drei Variablen mit vereinfachten Adressen skizziert (siehe Abbildung). Später soll die Box einen Schultag lang (7 Stunden) alle 10 Minuten einen Temperatur- und einen CO2-Wert speichern. Auf dem Mikrocontroller der Box stehen dafür 2048 Byte zur Verfügung.

Lucas Skizze: Speicher der Sensorbox (vereinfachte Adressen)
0x20000x20010x20020x20030x20040x20050x20060x20070x20080x20090x200A0x200B0x200C0x200D0x200E0x200F'B'raumco2tempandere Daten
Jedes Kästchen ist ein Byte; unter jedem Kästchen steht seine Adresse.
  1. Lesen Sie aus der Skizze die Adressen &raum, &co2 und &temp, die Anzahl der Bytes jeder Variable und die Adresse des letzten Bytes von temp ab.
  2. Berechnen Sie den Speicherbedarf der Messwerte für einen Schultag und für eine Schulwoche (5 Tage), wenn die Größen aus dem Testlauf gelten. Prüfen Sie, ob eine Woche in die 2048 Byte passt, und berechnen Sie den Bedarf erneut für den Fall, dass die Temperatur als float (4 Byte) gespeichert wird.
  3. Interpretieren Sie die Ausgabe der beiden Programmstarts: Was ändert sich, was bleibt gleich, und was bedeutet das?

Hinweise

Hinweis zu Aufgabe a)
Die Adresse einer Variable ist die Adresse ihres ersten Bytes. Zählen Sie die Kästchen jeder Farbe.
Hinweis zu Aufgabe b)
Wie viele Messzeitpunkte hat eine Stunde, wie viele ein Tag mit 7 Stunden? Jeder Messzeitpunkt braucht einen double und einen int.
Hinweis zu Aufgabe c)
Rechnen Sie in Hexadezimal: Wie weit liegen co2 und temp jeweils von raum entfernt? Vergleichen Sie diese Abstände mit den Größen aus der ersten Zeile.„Interpretieren“ heißt: die Zahlen auf die Sache beziehen und in Worten sagen, was sie bedeuten.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)
VariableAdresseBytesbelegt
raum0x200310x2003
co20x200440x2004 bis 0x2007
temp0x200880x2008 bis 0x200F

Das letzte Byte von temp liegt bei 0x200F (0x2008 + 7). Die Adresse einer Variable ist immer die ihres ersten Bytes.

Erwartungshorizont zu Aufgabe b)

Messzeitpunkte: 7 h · 6 pro Stunde = 42 pro Tag. Pro Zeitpunkt: 8 Byte (double) + 4 Byte (int) = 12 Byte.

  • Tag: 42 · 12 Byte = 504 Byte
  • Woche: 5 · 504 Byte = 2520 Byte > 2048 Byte — passt nicht.
  • mit float: 4 + 4 = 8 Byte pro Zeitpunkt; Tag 42 · 8 = 336 Byte, Woche 5 · 336 = 1680 Byte ≤ 2048 Byte — passt.

Die Größen 1, 4 und 8 Byte sind typische Werte (so auch im Testlauf); auf anderen Systemen können sie abweichen, deshalb nutzt man sizeof.

Erwartungshorizont zu Aufgabe c)
  • Gleich bleiben die Größen (1, 4, 8 Byte) und die Abstände: co2 liegt 1 Byte hinter raum (…49b → …49c), temp 4 Byte hinter co2 (…49c + 4 = …4a0). Die Variablen liegen hier also direkt hintereinander, jede so groß wie ihr Typ.
  • Anders sind die Adressen selbst (…2078549b gegenüber …c795752b). Das Betriebssystem legt den Speicherbereich eines Programms bei jedem Start neu fest; man kann sich also nicht auf eine bestimmte Adresse verlassen.
  • Die Adressen werden hexadezimal mit %p ausgegeben; Lucas Skizze mit 0x2000 ist eine Vereinfachung dieser langen Adressen.

Vollständig ist die Antwort mit beiden Beobachtungen, einer Hex-Rechnung als Beleg und der Deutung „Adressen ändern sich bei jedem Start, die Lage untereinander und die Größen nicht“.

2

Der Vokabeltrainer stürzt ab

AFB II–III

Lea und Ben programmieren einen Vokabeltrainer für den Französischunterricht. Nach einer Runde mit 20 Vokabeln soll man eingeben, wie viele man richtig hatte, und das Programm gibt die Trefferquote aus. Ben hat den ersten Entwurf geschrieben:

C · vokabel.c
#include <stdio.h>

int main(void) {
    int richtig = 0;
    int gesamt = 20;

    printf("Richtige Antworten: ");
    scanf("%d", richtig);
    printf("Quote: %.1f %%\n", 100.0 * richtig / gesamt);
    return 0;
}
Konsole
$ gcc -Wall vokabel.c -o vokabel
vokabel.c: In function 'main':
vokabel.c:8:13: warning: format '%d' expects argument of type 'int *', but argument 2 has type 'int' [-Wformat=]
    8 |     scanf("%d", richtig);
      |            ~^   ~~~~~~~
      |             |   |
      |             |   int
      |             int *
$ ./vokabel
Richtige Antworten: 17
Segmentation fault

Beim Nachdenken über den Absturz stellt Lea eine Behauptung auf: „Variablen liegen im Speicher immer in der Reihenfolge, in der sie deklariert sind — die zuerst deklarierte hat die kleinste Adresse, und dahinter kommen die anderen lückenlos.“

  1. Werten Sie die Warnung des Compilers und das Verhalten beim Programmstart aus, und korrigieren Sie das Programm. Notieren Sie auch die richtige Ausgabe für die Eingabe 17.
  2. Entwickeln Sie ein C-Programm, mit dem man Leas Behauptung experimentell prüfen kann. Legen Sie fest, woran man in der Ausgabe erkennt, ob die Behauptung zutrifft.
  3. Ben führt ein solches Testprogramm mit den Variablen char a, int b, char c, double d (in dieser Reihenfolge deklariert) aus und erhält:
    Konsole
    $ ./adresstest
    a: 0x7ffdfd617eda (1 Byte)
    b: 0x7ffdfd617edc (4 Byte)
    c: 0x7ffdfd617edb (1 Byte)
    d: 0x7ffdfd617ee0 (8 Byte)
    Widerlegen Sie Leas Behauptung mithilfe dieser Ausgabe.

Hinweise

Hinweis zu Aufgabe a)
Was erwartet %d bei scanf laut Warnung (int *), und was bekommt es stattdessen? Welchen Wert hat richtig in diesem Moment, und was macht scanf mit diesem Wert?
Hinweis zu Aufgabe b)
Deklarieren Sie mehrere Variablen verschiedener Typen und geben Sie jeweils Adresse (%p) und Größe (sizeof) aus. Was müsste für jede Variable gelten, wenn Lea recht hätte?„Entwickeln“ heißt: ein Vorgehen selbst entwerfen und die Schritte begründen — hier ein Testprogramm plus Auswertungsregel.
Hinweis zu Aufgabe c)
Sortieren Sie die vier Variablen nach ihrer Adresse und vergleichen Sie mit der Reihenfolge der Deklaration. Ein einziges Gegenbeispiel genügt.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

Die Warnung ist ernst zu nehmen: scanf muss den eingelesenen Wert in die Variable schreiben und braucht dafür deren Adresse (Typ int *). Ben übergibt aber den Wert von richtig, also 0. scanf behandelt diese 0 als Adresse und versucht, die 17 an Adresse 0 zu schreiben. Dieser Speicher gehört nicht zum Programm, das Betriebssystem bricht ab: „Segmentation fault“ (Speicherzugriffsfehler). Die Warnung beim Übersetzen hat den Fehler also schon angekündigt.

C · vokabel.c (korrigiert, mit gcc -Wall ohne Warnung)
#include <stdio.h>

int main(void) {
    int richtig = 0;
    int gesamt = 20;

    printf("Richtige Antworten: ");
    scanf("%d", &richtig);                  // Adresse von richtig übergeben
    printf("Quote: %.1f %%\n", 100.0 * richtig / gesamt);
    return 0;
}
Konsole (mit gcc geprüft)
$ ./vokabel
Richtige Antworten: 17
Quote: 85.0 %

Rechnung: 100,0 · 17 / 20 = 85,0.

Erwartungshorizont zu Aufgabe b)
C · adresstest.c (mit gcc getestet)
#include <stdio.h>

int main(void) {
    char a = 'x';            // bewusst gemischte Typen
    int b = 1;
    char c = 'y';
    double d = 2.5;

    printf("a: %p (%zu Byte)\n", &a, sizeof(a));
    printf("b: %p (%zu Byte)\n", &b, sizeof(b));
    printf("c: %p (%zu Byte)\n", &c, sizeof(c));
    printf("d: %p (%zu Byte)\n", &d, sizeof(d));
    return 0;
}

Auswertung: Die Variablen nach Adressen sortieren. Lea hätte recht, wenn (1) die Reihenfolge der Adressen der Reihenfolge der Deklaration entspricht und (2) jede Adresse genau um die Größe der vorherigen Variable größer ist als deren Adresse (keine Lücken). Sinnvoll sind gemischte Typen, weil der Compiler Variablen oft so anordnet, dass z. B. int und double an „glatten“ Adressen beginnen. Das Programm sollte mehrmals gestartet werden: Die Adressen ändern sich, das Muster nicht.

Erwartungshorizont zu Aufgabe c)
Adresse (Ende)Variabledeklariert als
…edaa (1 Byte)1.
…edbc (1 Byte)3.
…edc bis …edfb (4 Byte)2.
…ee0 bis …ee7d (8 Byte)4.

c wurde nach b deklariert, liegt aber an einer kleineren Adresse (…edb < …edc). Damit ist die Behauptung „immer in der Reihenfolge der Deklaration“ durch ein Gegenbeispiel widerlegt. Der Compiler darf die Variablen frei anordnen; hier hat er die beiden char zusammengelegt, sodass b an einer durch 4 teilbaren Adresse beginnt. (Lückenlos sind sie in diesem Lauf zufällig trotzdem — das ändert nichts am Gegenbeispiel.)

Vollständig ist die Antwort mit dem konkreten Adressvergleich und dem Hinweis, dass ein Gegenbeispiel genügt.