MINT lernen

Übung — AFB II (Zusammenhänge herstellen)

Zehn Aufgaben zum Anwenden — Code verfolgen, Fehler finden, Werte ergänzen, quer durch C und C++.

Dein Fortschritt:
0 / 0 Aufgaben
2

Aufgabenblock — AFB II

Zehn Aufgaben zum Anwenden: Code Schritt für Schritt verfolgen, Fehler finden, fehlende Werte ergänzen. Nutze die gestuften Tipps erst, wenn du wirklich festhängst — und schreib die Tracetabellen auf Papier mit, wie in der Arbeit.

A1
Eine while-Schleife verfolgen
AFB II
C
int n = 6;
int schritte = 0;
while (n != 1) {
    if (n % 2 == 0) {
        n = n / 2;
    } else {
        n = 3 * n + 1;
    }
    schritte++;
}
printf("%d\n", schritte);

Stelle den Ablauf als Tracetabelle mit den Spalten n und schritte dar. Welcher Wert wird ausgegeben?

Schreibe vor jeder Prüfung der Bedingung n != 1 den aktuellen Wert von n auf.
Gerade Zahlen werden halbiert, ungerade werden zu 3 · n + 1. Beginne mit 6 → 3 → 10 → …
Folge: 6, 3, 10, 5, 16, 8, 4, 2, 1 — zähle die Pfeile.
Vollständige Lösung
schritte012345678
n63105168421

Bei n = 1 ist die Bedingung n != 1 falsch, die Schleife endet. → Ausgabe 8

A2
Ein Mittelwert, der nicht stimmt
AFB II
C
int werte[4] = {18, 21, 19, 23};
int summe = 0;
for (int i = 0; i < 4; i++) {
    summe += werte[i];
}
double mittel = summe / 4;
printf("%.2f\n", mittel);

Die Wetter-AG will die mittlere Temperatur von vier Messungen ausgeben. Erkläre, warum das Programm einen falschen Wert anzeigt. Gib den ausgegebenen und den richtigen Mittelwert an.

Welchen Typ haben summe und 4? Welche Division rechnet C dann?
Die Summe ist 81. 81 / 4 wird als Ganzzahldivision gerechnet, bevor das Ergebnis in double gespeichert wird.
Ausgabe 20.00; richtig ist 81 / 4 = 20,25.
Vollständige Lösung

18 + 21 + 19 + 23 = 81. Weil summe und 4 beide int sind, rechnet 81 / 4 ganzzahlig = 20. Erst danach wird 20 in mittel umgewandelt — die Nachkommastellen sind schon verloren. Ausgabe: 20.00.

Korrektur mit Cast vor der Division:

C
double mittel = (double) summe / 4;

Ausgabe 20.25

A3
Fehler in einer Funktion
AFB II
C · durchschnitt.c
 1#include <stdio.h>
 2
 3double durchschnitt(int a, int b) {
 4    return (a + b) / 2;
 5}
 6
 7int main(void) {
 8    printf("%.1f\n", durchschnitt(7, 8));
 9    return 0;
10}

Die Funktion soll den Durchschnitt zweier ganzer Zahlen liefern. Überprüfe das Programm: Welche Zahl gibt es aus, und in welcher Zeile steckt der Fehler?

Der Rückgabetyp ist zwar double, aber rechne nach, welche Typen in Zeile 4 zusammentreffen.
(7 + 8) / 2 = 15 / 2 — beide Operanden sind int.
Ganzzahldivision 15 / 2 = 7, Ausgabe 7.0; Zeile 4.
Vollständige Lösung

(a + b) / 2 rechnet mit zwei int-Werten: 15 / 2 = 7. Erst beim return wird 7 in 7.0 umgewandelt. Der Rückgabetyp double allein rettet nichts.

Fehler in Zeile 4. Richtig: return (a + b) / 2.0; oder return (double) (a + b) / 2; → Ausgabe 7.5.

A4
Wo liegen die Array-Elemente?
AFB II
C
double w[5];
printf("%zu\n", sizeof(w));
printf("%p\n", (void *) &w[0]);
printf("%p\n", (void *) &w[3]);

Eine Ausführung liefert für die zweite Zeile 0x7ffc3a51e2c0. Nimm die typische Größe 8 Byte für double an. Ermittle den Wert der ersten Ausgabezeile und den Abstand in Byte zwischen den Adressen in Zeile 2 und 3.

Byte
Ein Array liegt lückenlos im Speicher: Element für Element direkt hintereinander.
w[3] ist das vierte Element. Davor liegen w[0], w[1], w[2].
5 · 8 Byte = 40 Byte; 3 · 8 Byte = 24 Byte.
Vollständige Lösung

sizeof(w) = 5 · 8 Byte = 40.

Zwischen &w[0] und &w[3] liegen die drei Elemente w[0], w[1], w[2]: 3 · 8 Byte = 24 Byte. Die dritte Zeile lautet also 0x7ffc3a51e2d8 (hex: c0 + 18 = d8). Bei jedem Start können die Adressen anders sein — der Abstand bleibt gleich.

A5
Zeiger und Wert als Parameter
AFB II
C
#include <stdio.h>

void aendere(int *p, int q) {
    *p = *p + 10;
    q = q + 10;
}

int main(void) {
    int a = 1, b = 2;
    aendere(&a, b);
    printf("%d %d\n", a, b);
    return 0;
}

Untersuche, welche der beiden Variablen die Funktion wirklich verändert. Gib die Ausgabe an.

Beim ersten Parameter wird eine Adresse übergeben, beim zweiten ein Wert.
*p = *p + 10 schreibt an die Adresse von a. q ist eine Kopie von b.
a wird 11, b bleibt 2.
Vollständige Lösung
Variablevor dem Aufrufin aenderenach dem Aufruf
a1*p → 1111
b2q → 12 (Kopie)2
Ausgabe
11 2

Genau deshalb braucht scanf die Adresse &x: Nur über einen Zeiger kann eine Funktion eine Variable des Aufrufers ändern.

A6
Zeichen zählen
AFB II
C
char ort[] = "Wetterstation";
int n = 0;
for (int i = 0; ort[i] != '\0'; i++) {
    if (ort[i] == 't') {
        n++;
    }
}
printf("%d\n", n);

Werte die Schleife aus: Welche Zahl wird ausgegeben? Wie viele Byte belegt das Array ort?

Byte
Die Schleife läuft, bis sie auf das Endezeichen '\0' trifft.
Schreibe das Wort mit Indizes auf: W0 e1 t2 t3 e4 r5 s6 t7 a8 t9 i10 o11 n12.
t steht bei Index 2, 3, 7 und 9; 13 Zeichen + Endezeichen.
Vollständige Lösung
Index012345678910111213
ZeichenWetterstation\0

Vier Treffer → Ausgabe 4. Das Array fasst 13 Zeichen und das Endezeichen → 14 Byte. Großes „T“ würde übrigens nicht mitgezählt: C unterscheidet Groß- und Kleinbuchstaben.

A7
Blinken per Taster
AFB II
Arduino-Sketch
void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(8, OUTPUT);
}

void loop() {
  if (digitalRead(2) == LOW) {
    digitalWrite(8, HIGH);
    delay(100);
    digitalWrite(8, LOW);
    delay(150);
  } else {
    digitalWrite(8, LOW);
  }
}

Der Taster an Pin 2 ist ohne Widerstand direkt gegen GND angeschlossen. Erläutere das Verhalten des Sketches. Wie oft blinkt die LED, wenn der Taster 6 s lang gedrückt gehalten wird, und mit welcher Frequenz?

Hz
Mit INPUT_PULLUP liest der Pin HIGH, solange nichts gedrückt ist. Gedrückt verbindet der Taster mit GND.
Während der Taster gedrückt ist, läuft in jedem loop()-Durchlauf genau eine Periode: 100 ms an + 150 ms aus.
T = 250 ms, also 4 Perioden pro Sekunde; 6 s · 4 = 24.
Vollständige Lösung

Gedrückt liefert digitalRead(2) den Wert LOW, also läuft der if-Zweig: Eine Periode dauert T = 100 ms + 150 ms = 250 ms = 0,25 s.

f = 1 / T = 1 / 0,25 s = 4 Hz; in 6 s: 6 · 4 = 24-mal. Losgelassen ist der Pin HIGH, der else-Zweig hält die LED aus.

A8
Referenz oder Kopie?
AFB II
C++
#include <iostream>
using namespace std;

void punkteDazu(int &konto, int bonus) {
    konto += bonus;
    bonus = 0;
}

int main() {
    int stand = 40, extra = 15;
    punkteDazu(stand, extra);
    punkteDazu(stand, extra);
    cout << stand << " " << extra << endl;
    return 0;
}

Vergleiche die beiden Parameter von punkteDazu: Welche Werte haben stand und extra am Ende?

Achte auf das & in der Parameterliste: int &konto ist eine Referenz, int bonus eine Kopie.
Jeder Aufruf addiert 15 zu stand. bonus = 0 ändert nur die Kopie.
40 + 15 + 15 = 70; extra bleibt 15.
Vollständige Lösung

konto ist ein zweiter Name für stand: Jeder Aufruf erhöht stand um 15 → 55 → 70.

bonus ist eine Kopie von extra. bonus = 0 setzt nur die Kopie zurück; extra bleibt 15. Auch beim zweiten Aufruf wird deshalb wieder 15 übergeben.

Ausgabe
70 15
A9
Ein Objekt verfolgen
AFB II
C++
class Konto {
private:
    int stand;
public:
    Konto(int start) { stand = start; }
    void einzahlen(int b) {
        if (b > 0) stand += b;
    }
    bool abheben(int b) {
        if (b > stand) return false;
        stand -= b;
        return true;
    }
    int getStand() { return stand; }
};

int main() {
    Konto k(50);
    k.einzahlen(30);
    k.abheben(100);
    k.einzahlen(-20);
    k.abheben(45);
    cout << k.getStand() << endl;
    return 0;
}

Analysiere die Methodenaufrufe in main. Welcher Kontostand wird ausgegeben? Wie viele der Aufrufe verändern den Kontostand nicht?

Gehe Aufruf für Aufruf durch und notiere den Wert von stand.
abheben(100) bei Stand 80 und einzahlen(-20) scheitern an ihren if-Bedingungen.
50 → 80 → 80 → 80 → 35; zwei Aufrufe ohne Wirkung.
Vollständige Lösung
AufrufBedingungstand
Konto k(50)Konstruktor50
einzahlen(30)30 > 0 ✓80
abheben(100)100 > 80 → false80
einzahlen(-20)−20 > 0 ✗80
abheben(45)45 > 80 ✗ → abheben35

→ Ausgabe 35, zwei wirkungslose Aufrufe. Weil stand private ist, kann main den Stand nur über die Methoden ändern — so bleiben ungültige Werte draußen (Kapselung).

A10
Zufallszahlen im Wunschbereich
AFB II

Für ein Würfelspiel mit einem Spezialwürfel braucht ihr ganze Zufallszahlen von 5 bis 12 (beide einschließlich). Die Anweisung hat die Form int z = a + rand() % n;

Leite her, welche Zahl für n einzusetzen ist. Wie viele Tipps braucht man beim Zahlenraten im Bereich 1 bis 500 höchstens, wenn man immer die Mitte des verbleibenden Bereichs tippt?

Tipps
rand() % n liefert 0 bis n − 1. Welcher Wert kommt heraus, wenn man a addiert?
a = 5 (kleinster Wert). Aus a + n − 1 = 12 folgt n. Beim Halbieren: Nach k Tipps sind bis zu 2k Zahlen erledigt.
n = 12 − 5 + 1 = 8; 28 = 256 < 500 ≤ 512 = 29.
Vollständige Lösung

rand() % n liefert 0 … n − 1, also 5 + rand() % n die Werte 5 … 5 + n − 1.

5 + n − 1 = 12 ⇔ n = 12 − 5 + 1 = 8 → int z = 5 + rand() % 8;

Jeder Tipp halbiert den Bereich. Mit k Tipps findet man die Zahl sicher, wenn 2k ≥ 500: 28 = 256 reicht nicht, 29 = 512 reicht → höchstens 9 Tipps.