MINT lernen

Textaufgaben: Arrays und Zeichenketten

Eine Schul-Wetterstation zählt eine Woche Temperaturen durch, und ein Vokabeltrainer lernt, Wörter richtig zu vergleichen.

Dein Fortschritt:
0 / 0 Aufgaben
1

Eine Woche Wetterdaten

AFB I–II

Die Wetter-AG hat auf dem Schuldach eine Messstation aufgebaut. Jeden Mittag wird die Temperatur in Grad Celsius gespeichert. Mila schreibt ein C-Programm, das für eine Woche den Mittelwert und den wärmsten Tag ausgibt.

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

int main(void)
{
    double temp[7] = {14.5, 17.0, 12.5, 19.5, 21.0, 16.5, 18.0};
    int n = sizeof(temp) / sizeof(temp[0]);
    int imax = 0;
    double summe = 0.0;
    for (int i = 0; i < n; i++) {
        summe = summe + temp[i];
        if (temp[i] > temp[imax]) {
            imax = i;
        }
    }
    printf("Mittelwert: %.1f Grad\n", summe / n);
    printf("Maximum: %.1f Grad am Tag %d\n", temp[imax], imax + 1);
    return 0;
}

Ihr Mitschüler Paul ändert die Schleifenbedingung zu i <= n, „damit auch der letzte Tag sicher dabei ist“. Das Programm lässt sich ohne Warnung übersetzen. Beim ersten Start erscheint als Mittelwert eine riesige negative Zahl mit über 80 Stellen, beim zweiten Start wieder 17.0.

Das Array im Speicher
double temp[7]Tag 114.5[0]Tag 217.0[1]Tag 312.5[2]Tag 419.5[3]Tag 521.0[4]Tag 616.5[5]Tag 718.0[6]Index
Die sieben Werte liegen lückenlos hintereinander; über jeder Zelle steht der Tag, darunter der Index.
  1. Geben Sie den Wert von temp[2], den größten zulässigen Index sowie die Werte von sizeof(temp) und n an (typische Größen).
  2. Ermitteln Sie die Ausgabe des Programms. Halten Sie dabei fest, bei welchem Schleifendurchlauf sich imax ändert.
  3. Begründen Sie, warum der Compiler Pauls Fehler nicht meldet und warum sich das Programm bei jedem Start anders verhalten kann.

Hinweise

Hinweis zu Aufgabe a)
Der Index beginnt bei 0. Ein double belegt typisch 8 Byte.
Hinweis zu Aufgabe b)
Legen Sie eine Tabelle mit den Spalten i, temp[i], summe und imax an. imax ändert sich nur, wenn ein Wert größer als das bisherige Maximum ist.
Hinweis zu Aufgabe c)
Welcher Index wird im letzten Durchlauf mit i <= n benutzt? Prüft C beim Zugriff temp[i], ob der Index gültig ist?

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

temp[2] = 12.5 (dritter Tag). Größter zulässiger Index: 6. sizeof(temp) = 7 · 8 Byte = 56 Byte (typisch), n = 56 / 8 = 7.

Erwartungshorizont zu Aufgabe b)
itemp[i]summeimax
014.514.50
117.031.51
212.544.01
319.563.53
421.084.54
516.5101.04
618.0119.04

imax ändert sich bei i = 1, 3 und 4. Mittelwert: 119.0 / 7 = 17.0.

Ausgabe
Mittelwert: 17.0 Grad
Maximum: 21.0 Grad am Tag 5
Erwartungshorizont zu Aufgabe c)

Mit i <= n läuft die Schleife bis i = 7 und liest temp[7]. Dieses Element gibt es nicht: Das Array hat nur die Indizes 0 bis 6.

C prüft Array-Grenzen nicht. temp[7] ist für den Compiler nur „Anfangsadresse von temp plus 7 · 8 Byte“ — syntaktisch korrekt, also übersetzt er ohne Fehler. Zur Laufzeit wird gelesen, was zufällig hinter dem Array im Speicher liegt (undefiniertes Verhalten). Das kann bei jedem Start etwas anderes sein, daher einmal eine absurde Zahl in der Summe und einmal ein unauffälliges Ergebnis. Richtig ist i < n.

2

Neles Vokabeltrainer

AFB II–III

Nele programmiert für ihre Schwester in Klasse 5 einen Vokabeltrainer. Ihr erster Test mit einem einzigen Wort funktioniert nicht: Auch wenn sie genau house eintippt, meldet das Programm „Leider falsch“.

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

int main(void)
{
    char loesung[] = "house";
    char eingabe[20];
    printf("Haus auf Englisch: ");
    scanf("%s", eingabe);
    if (eingabe == loesung) {
        printf("Richtig!\n");
    } else {
        printf("Leider falsch, richtig ist %s.\n", loesung);
    }
    return 0;
}
Übersetzen mit gcc -Wall vokabel.c -o vokabel
vokabel.c:9:17: warning: comparison between two arrays [-Warray-compare]
    9 |     if (eingabe == loesung) {
      |                 ^~

Später sollen drei Vokabeln abgefragt werden; bei einer falschen Antwort soll das Programm zusätzlich verraten, wie viele Buchstaben die Lösung hat. Nele ist sicher: „char eingabe[20] reicht immer, so lange englische Wörter tippt in Klasse 5 niemand.“

  1. Überprüfen Sie Neles Vergleich eingabe == loesung mithilfe der Warnung und korrigieren Sie die Zeile.
  2. Erweitern Sie das Programm um eine Funktion int frage(char deutsch[], char englisch[]), die eine Vokabel abfragt und 1 (richtig) oder 0 (falsch) zurückgibt; bei falscher Antwort nennt sie die Lösung und deren Buchstabenzahl. main fragt drei Vokabeln ab und gibt aus, wie viele richtig waren.
  3. Nehmen Sie Stellung zu Neles Aussage über char eingabe[20].

Hinweise

Hinweis zu Aufgabe a)
Wofür steht der Name eines Arrays, wenn er ohne Index in einem Ausdruck vorkommt? Welche Funktion aus <string.h> vergleicht Zeichenketten, und was liefert sie bei Gleichheit?
Hinweis zu Aufgabe b)
Die Eingabe-Variable gehört jetzt in die Funktion. Die Buchstabenzahl liefert strlen (ohne '\0'); für %d können Sie das Ergebnis mit (int) umwandeln. In main genügt ein Zähler, zu dem die Rückgabewerte addiert werden.
Hinweis zu Aufgabe c)
Wie viele Buchstaben passen wirklich in 20 Byte, wenn am Ende '\0' stehen muss? Was passiert bei einer längeren Eingabe, wenn C keine Grenzen prüft? Denken Sie auch an Eingaben, die gar keine Vokabeln sind.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

Ein Array-Name steht für die Adresse seines ersten Elements. eingabe == loesung vergleicht deshalb die Anfangsadressen zweier verschiedener Arrays — die sind nie gleich, egal was eingetippt wurde. Genau darauf weist die Warnung „comparison between two arrays“ hin.

Korrektur: #include <string.h> ergänzen und if (strcmp(eingabe, loesung) == 0) schreiben — strcmp vergleicht Zeichen für Zeichen und liefert 0 bei Gleichheit.

Übrigens steht auch in scanf("%s", eingabe) zu Recht kein &: eingabe ist schon eine Adresse.

Erwartungshorizont zu Aufgabe b)
C · vokabel.c (erweitert)
#include <stdio.h>
#include <string.h>

int frage(char deutsch[], char englisch[])
{
    char eingabe[20];
    printf("%s auf Englisch: ", deutsch);
    scanf("%19s", eingabe);
    if (strcmp(eingabe, englisch) == 0) {
        printf("Richtig!\n");
        return 1;
    }
    printf("Falsch, richtig ist %s (%d Buchstaben).\n",
           englisch, (int) strlen(englisch));
    return 0;
}

int main(void)
{
    int richtig = 0;
    richtig = richtig + frage("Haus", "house");
    richtig = richtig + frage("Baum", "tree");
    richtig = richtig + frage("Schmetterling", "butterfly");
    printf("%d von 3 richtig\n", richtig);
    return 0;
}
Probelauf (Eingaben: house, tre, butterfly)
Haus auf Englisch: house
Richtig!
Baum auf Englisch: tre
Falsch, richtig ist tree (4 Buchstaben).
Schmetterling auf Englisch: butterfly
Richtig!
2 von 3 richtig

Bewertet werden: Vergleich mit strcmp, strlen für die Buchstabenzahl, Rückgabewert 1/0, Zählen in main. Die Breitenangabe in %19s gehört zu Teilaufgabe c).

Erwartungshorizont zu Aufgabe c)

In char eingabe[20] passen höchstens 19 Zeichen, denn das Endezeichen '\0' braucht ein eigenes Byte. Für echte Vokabeln reicht das meist (auch „butterfly“ hat nur 9 Buchstaben).

Aber: scanf("%s", …) schreibt ohne Prüfung weiter, wenn jemand länger tippt — aus Versehen, im Spaß („aaaaaaaaaaaaaaaaaaaaaaaaaaaa“) oder weil ein langes deutsches Wort wie „Donaudampfschifffahrt“ (21 Zeichen) eingegeben wird. Dann werden Bytes hinter dem Array überschrieben: undefiniertes Verhalten, im schlimmsten Fall Absturz oder eine Sicherheitslücke.

Stellungnahme: Die Aussage ist nur im Normalfall richtig. Ein Programm darf sich nicht auf brave Eingaben verlassen; mit scanf("%19s", eingabe) wird die Länge sicher begrenzt. Eine vollständige Antwort nennt die 19-Zeichen-Grenze, die Folge einer zu langen Eingabe und eine Absicherung.