Block 4 - Zeiger und Datenstrukturen

Initialisierung von Datenstrukturen

Übungen

1.) Bodenfeuchtigkeit

Definieren Sie ein Struct, welches die Bodenfeuchtigkeit (float) sowie die X- und Y-Koordinaten (int) speichert.

2.) Raumtemperatur

Definieren Sie ein Struct, welches die Raumnummer (String) sowie die Temperatur (float) speichert.

3.) Person

Es sollen folgende Daten einer Person gespeichert werden:

  • Name (String)

  • Raumnummer (String)

  • Alter (unsigned int)

  • Einstellungsjahr (int)

​​​​​​​Definieren Sie das passende Struct.

4.) Datenzugriff

Wir verwenden nun das Struct aus der vorherigen Aufgabe (Person). Sie haben bereits eine Instanz davon definiert und „meyer“ genannt. Diese Person ist nun ein Jahr älter geworden. Wie ändern Sie das Alter (nur das Alter)?

5.) Strings in Strukturen Initialisieren

Wenn Sie strings in einem struct verwenden, müssen Sie diese etwas komplizierter initialisieren. Dafür benutzt man oft die Funktion strcnpy(). Recherchieren Sie selbstständig, wie man diese nutzt. Initialisieren Sie nun den kompletten struct „meyer“.

6.) String

Eine andere Möglichkeit, strings zu erzeugen, ist String zu benutzen. Das ist strenggenommen eine Klasse, die wir erst im zweiten Semester behandeln werden, aber man kann sie hier gut nutzen, auch ohne die komplette Theorie dahinter zu kennen. Recherchieren Sie selbstständig, wie man sie nutzt und schreiben Sie Ihr Code von der vorherigen Aufgabe damit um. Sie müssen auch die Definition der Struktur ändern.

7.) Komplexe Zahlen

Definieren Sie ein Struct, in dem Sie eine komplexe Zahl (re, im: int) speichern können.

8.) Temperaturdaten

Erstellen Sie eine Datenstruktur, die folgende Angaben zu Temperaturdaten enthält: • Ort der Messung (String, z.B. „Bremen“) • Datum (vorzeichenlose 32-Bit-Ganzzahl, z.B. 20191120 für den 20. November 2019) • Uhrzeit (vorzeichenlose 16-Bit-Ganzzahl, z.B. 1430 für 14:30 Uhr) • Temperatur in Grad Celsius (Fließkommazahl)

Lösungen

Lösung zu Aufgabe 1.):

struct Feuchtigkeit {
  int X;
  int Y;
  float Feuchtigkeit;
};

Lösung zu Aufgabe 2.):

struct Raum {
  String Raumnummer;
  float Temperatur;
};

Lösung zu Aufgabe 3.):

struct Person {
  String Name;
  String Raumnummer;
  unsigned int Alter;
  int Einstellungsjahr;
};

Lösung zu Aufgabe 4.):

meyer.Alter++;

Lösung zu Aufgabe 5.):

Person meyer;
strncpy(meyer.Name, „Raul Meyer“, sizeof(meyer.Name));
strncpy(meyer.Raumnummer, „NW5020“, sizeof(meyer.Raumnummer));
meyer.Alter = 35;
meyer.Einstellungsjahr = 2000;

Lösung zu Aufgabe 6.):

struct Person {
  String Name;
  String Raumnummer;
  unsigned int Alter;
  int Einstellungsjahr;
};
Person meyer = {„Raul Meyer“, „NW5020“, 35, 2000};

Lösung zu Aufgabe 7.):

struct Complex {
  int Re;
  int Im;
};

Datenstrukturen programmieren

Video einfügen: "Code Beispiel: Struct"

Kommentiertes Beispiel

Unten sehen Sie ein Beispiel, wie mit Datenstrukturen programmiert wird. Schauen Sie sich das Beispiel an und verstehen Sie, wie der Code funktioniert.

/**
 * Dieses Beispiel erklärt die grundlegende Verwendung von Structs (Deutsch: Verbund)
 * Structs werden generell immer dann verwenden, wenn man mehrere
 * zusammengehörige Variablen bzw. Werte speicher möchte.
 * Dies geschieht z.B. in folgenden Fällen:
 * - RGB-Werte für eine RGB-LED
 * - Speicherung zusammengehörigen Sensor / Messwerte
 * - ...
 * 
 * In diesem Beispiel nehmen wir an, dass wir Messwerte speichern möchten.
 * Diese bestehen aus folgenden Werten:
 * - Die Temperatur (float)
 * - Die Feuchtigkeit (float)
 * - Eine kurze (9 Zeichen) Beschreibung des jeweiligen Sensors
 * 
 * Zur Eingabe der Daten in das Struct nutzen wir die Funktion
 * "getEnvironmentValues()", für die Ausgaben "printEnvironmentValues(Environment)".
 * 
 * Diese Beispiel lässt sich einfach so erweitern, dass reale Daten verarbeitet werden.
 */


// Definition des Structs Environment
struct Environment {
  float temperature = 0.0;  // Temperatur als float
  float humidity = 0.0;     // Feuchtigkeit als float
  char sensor_name[20];     // Name als char array, 19 Zeichen + 0-Terminator
};

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(100000000);
}

void loop() {
  // Lese die Werte vom Nutzer ein:
  Environment myEnvironment = getEnvironmentValues();

  // Gebe die eingegebenen Werte aus:
  printEnvironmentValues(myEnvironment);

  // Erzeuge eine Kopie:
  Environment myEnvironment_2 = myEnvironment;

  // Ändere die Temperatur:
  myEnvironment_2.temperature = 11.75;

  // setze einen anderen Namen für den Sensor
  strncpy(myEnvironment_2.sensor_name, "My Sensor 2", sizeof(myEnvironment_2.sensor_name));

  // Geben beiden Structs erneut aus
  printEnvironmentValues(myEnvironment_2);
  printEnvironmentValues(myEnvironment);
  
  delay(3000);
  

}

/**
 * Diese Funktion gibt alle Wertes eines Environment-Structs aus
 */
void printEnvironmentValues(Environment env){
  Serial.println("###");
  Serial.print("Temperatur: ");
  Serial.println(env.temperature);
  Serial.print("Feuchtigkeit: ");
  Serial.println(env.humidity);
  Serial.print("Name: ");
  Serial.println(env.sensor_name);
  Serial.println("***");
}


/**
 * Diese Funktion fragt vom Nutzer die Temperatur, Feuchtigkeit
 * sowie den Sensornamen ab und liefert das entsprechende Struct
 * als Rückgabewert.
 * 
 * Diese Funktion kann entsprechend umgewandelt werden, so dass
 * sie Messwerte von realen (am Arduino angeschlossenen) Sensor
 * liefert.
 * 
 * Zusatzaufgabe: Passen Sie die Funktion so an, dass sie einen
 * Sensor (z.B. DHT11, in vielen Kits vorhanden) ausliest und
 * zurück gibt.
 */
Environment getEnvironmentValues(){

  // Hilfsvariablen
  char buffer[20];
  int len = 0;

  // Die Environment, die wir später an das Hauptprogramm zurück geben
  Environment env;
  
  Serial.print("Bitte die Temperatur eingeben:");
  Serial.flush();  // Sicher stellen, dass der Text ausgegeben wird
  len = Serial.readBytesUntil('\n', buffer, 19); // Daten einlesen
  Serial.println();
  buffer[len] = 0; // Nullterminierung
  env.temperature = atof(buffer); // Zeichenfolge in einen float umwandeln

  // vgl. Temperatur
  Serial.print("Bitte die Feuchtigkeit eingeben:");
  Serial.flush();
  len = Serial.readBytesUntil('\n', buffer, 19);
  Serial.println();
  buffer[len] = 0;
  env.humidity = atof(buffer);

  // Name des Sensors einlesen. Dieser wird direkt im Struct (nicht im buffer) gespeichert.
  Serial.print("Name des Sensors:");
  len = Serial.readBytesUntil('\n', env.sensor_name, 19);
  Serial.println();
  env.sensor_name[len] = 0; // Nullterminierung nicht vergessen

  return env; // Rückgabe des Structs an das Hauptprogramm
}

Übungsaufgaben

1.) Menschen

Erzeugen Sie ein Struct, welches das Geburtsjahr (int) sowie den Namen (String) eines Menschen enthält. Erzeugen Sie folgende Menschen:

  • Lieschen Müller, Geboren 1999

  • Max Mustermann, Geboren 1980

  • Willi Wolff, Geboren 2010

Geben Sie Namen und Geburtsjahr über den Seriellen Monitor aus.

2.) Menschen mit Funktion

Basierend auf der vorherigen Übung: Schreiben Sie eine Funktion, die Name und Alter der Personen ausgibt. Diese Funktion soll als Parameter das Struct vom Type Mensch erhalten und keinen Rückgabewert liefern. Nennen Sie die Funktion „printMensch“ und nutzen Sie diese, um die Structs erneut auszugeben.

3.) Menschen im Array

Sie können auch ein Array bestehend aus Structs erstellen. Ändern Sie das Programm aus dem vorhergegangenen Beispiel wie folgt ab:

  • Die drei Menschen werden in einem Array gespeichert.

  • Die Namen und die Geburtsdaten werden in einer for-Schleife ausgegeben.

4.) Menschen altern

Ändern Sie das Beispiel aus der vorherigen Übung wie folgt ab:

  • Statt des Geburtsjahres wird nun das aktuelle Alter ausgegeben

  • Alle Menschen altern in der loop()-Funktion 1 Jahr pro Sekunde. Geben Sie das Alter entsprechend aus.

5.) Menschen sterben...

Wir nehmen an, dass alle Menschen genau 100 Jahre alt werden. Passen Sie das vorherige Programm so an, dass Menschen über 100 Jahre nicht mehr ausgegeben werden. Markieren Sie diese als tot, indem Sie das Geburtsjahr auf -1 setzten.

6.) Menschen sterben 2.0

Passen Sie das vorherige Beispiel so an, dass der Zustand des Menschen in einer zusätzlichen Variable im Struct gespeichert wird. Das Geburtsjahr soll dabei erhalten bleiben.

7.) Komplexe Zahlen

Berechnen Sie das Quadrat der komplexen Zahl 4+j3 in Arduino. Nutzen Sie ein Struct zur Speicherung der Werte.

8.) Koordinaten

Erzeugen Sie ein Struct, welches eine X- und eine Y-Koordinate (beides int) speichert. Erzeugen Sie 4 Koordinaten und speichern Sie diese in einem Array:

  • X= 7, Y=12

  • X=2, Y=10

  • X=15, Y=1

  • X=1, Y=2

Geben Sie die Koordinaten aus. Schreiben Sie die Funktion printKoordinate für die Ausgabe. Der Parameter der Funktion ist die jeweilige Koordinate. Geben Sie alle Koordinaten in der loop()- Funktion aus.

9.) Distanz zwischen Koordinaten

Basierend auf dem vorherigen Beispiel: Schreiben Sie eine Funktion, welche die Distanz zwischen zwei Koordinaten bestimmt (Pythagoras). Geben Sie die Distanzen zwischen allen Koordinaten aus.

Hinweis: Die Distanz kann eine Nachkommastelle enthalten.

10.) Höchste Temperatur suchen

Gegeben ist ein Array mit 10 Elementen. Jedes Element enthält jeweils die Datenstruktur, die in Aufgabe 4.1erstellt wurde. a) Wie wird das Array deklariert? b) Schreiben Sie einen Codeschnipsel, der die Elemente als Tabelle auf dem seriellen Monitor ausgibt. EineTabellenzeile soll z.B. so aussehen: Bremen 20191120 1430 -1.6 Sie müssen kein vollständiges Programm schreiben, sondern nur den Code angeben, der die 10 Elementeausgibt. Verwenden Sie für den Zugriff auf das Array den im Aufgabenteil a) deklarierten Arraynamen.

Lösungen

Lösung zu Aufgabe 1.):

struct Mensch {
  String Name;
  int Geburtsjahr;
};
void setup() {
  Serial.begin(9600);
  struct Mensch lm = {"Lieschen Müller", 1999};
  struct Mensch mm = {"Max Mustermann", 1980};
  struct Mensch ww = {"Willi Wolff", 2010};
  Serial.print(lm.Name);
  Serial.print(": ");
  Serial.println(lm.Geburtsjahr);
  Serial.print(mm.Name);
  Serial.print(": ");
  Serial.println(mm.Geburtsjahr);
  Serial.print(ww.Name);
  Serial.print(": ");
  Serial.println(ww.Geburtsjahr);
}
void loop() {
  // put your main code here, to run repeatedly:
}

Lösung zu Aufgabe 2.):

void printPerson(struct Mensch m) {
  Serial.print(m.Name);
  Serial.print(": ");
  Serial.println(m.Geburtsjahr);
}

Lösung zu Aufgabe 3.):

struct Mensch {
  String Name;
  int Geburtsjahr;
};

struct Mensch menschen[3];

void setup() {
  Serial.begin(9600);

  menschen[0] = {"Lieschen Müller", 1999};
  menschen[1] = {"Max Mustermann", 1980};
  menschen[2] = {"Willi Wolff", 2010};
  
  for (int i = 0; i < 3; i++) {
    printPerson(menschen[i]);
  }
}

void printPerson(struct Mensch m) {
  Serial.print(m.Name);
  Serial.print(": ");
  Serial.println(m.Geburtsjahr);
}

void loop() {
  // put your main code here, to run repeatedly:
}

Lösung zu Aufgabe 4.):

int jahr = 2018;

struct Mensch {
  String Name;
  int Geburtsjahr;
};

struct Mensch menschen[3];

void setup() {
  Serial.begin(9600);
  menschen[0] = {"Lieschen Müller", 1999};
  menschen[1] = {"Max Mustermann", 1980};
  menschen[2] = {"Willi Wolff", 2010};
}

void printPerson(struct Mensch m) {
  Serial.print(m.Name);
  Serial.print(": ");
  Serial.println(jahr - m.Geburtsjahr);
}

void loop() {
  Serial.print("Jahr: ");
  Serial.println(jahr);
  for (int i = 0; i < 3; i++) {
    printPerson(menschen[i]);
  }
  jahr++;
  delay(1000);
}

Lösung zu Aufgabe 5.):

int jahr = 2018;
struct Mensch {
  String Name;
  int Geburtsjahr;
};

struct Mensch menschen[3];

void setup() {
  Serial.begin(9600);
  menschen[0] = {"Lieschen Müller", 1999};
  menschen[1] = {"Max Mustermann", 1980};
  menschen[2] = {"Willi Wolff", 2010};
}

void printPerson(struct Mensch m) {
  Serial.print(m.Name);
  Serial.print(": ");
  Serial.println(jahr - m.Geburtsjahr);
}

void loop() {
  Serial.print("Jahr: ");
  Serial.println(jahr);
  for (int i = 0; i < 3; i++) {
    if (menschen[i].Geburtsjahr > 0) {
      printPerson(menschen[i]);

      if (jahr - menschen[i].Geburtsjahr >= 100) {
        menschen[i].Geburtsjahr = -1;
        Serial.print(menschen[i].Name);
        Serial.println(" ist gerade gestorben");
      }
    }
  }
  
  jahr++;
  delay(200);
}

Lösung zu Aufgabe 6.):

int jahr = 2018;

struct Mensch {
  String Name;
  int Geburtsjahr;
  bool Lebt;
};

struct Mensch menschen[3];

void setup() {
  Serial.begin(9600);
  menschen[0] = {"Lieschen Müller", 1999, true};
  menschen[1] = {"Max Mustermann", 1980, true};
  menschen[2] = {"Willi Wolff", 2010, true};
}

void printPerson(struct Mensch m) {
  Serial.print(m.Name);
  Serial.print(": ");
  Serial.println(jahr - m.Geburtsjahr);
}

void loop() {
  Serial.print("Jahr: ");
  Serial.println(jahr);
  for (int i = 0; i < 3; i++) {
    if (menschen[i].Lebt) {
      printPerson(menschen[i]);

      if (jahr - menschen[i].Geburtsjahr >= 100) {
        menschen[i].Lebt = false;
        Serial.print(menschen[i].Name);
        Serial.println(" ist gerade gestorben");
      }
    }
  }
  
  jahr++;
  delay(200);
}

Lösung zu Aufgabe 7.):

struct Complex {
  int Re;
  int Im;
};

void setup() {
  Serial.begin(9600);

  struct Complex c = {5, 7};

  // Ausgabe der Zahl
  Serial.print("(");
  Serial.print(c.Re);
  Serial.print("+");
  Serial.print(c.Im);
  Serial.print("j)^2=");
  
  // Berechnung und Ausgabe Ergebnis
  Serial.print(c.Re * c.Re – c.Im * c.Im);
  Serial.print("+");
  Serial.print(2 * c.Re * c.Im);
  Serial.println("j");
}

void loop() {
  // put your main code here, to run repeatedly:
}c

Lösung zu Aufgabe 8.):

struct Koordinate {
  int X;
  int Y;
};

struct Koordinate koordinaten[4];

void setup() {
  Serial.begin(9600);

  koordinaten[0] = {7, 12};
  koordinaten[1] = {2, 10};
  koordinaten[2] = {15, 1};
  koordinaten[3] = {1, 2};
}

void printKoordinate(struct Koordinate k) {
  Serial.print("X=");
  Serial.print(k.X);
  Serial.print(", Y=");
  Serial.println(k.Y);
}

void loop() {
  for (int i = 0; i < 4; i++) {
    printKoordinate(koordinaten[i]);
  }
  Serial.println("");
  delay(1000);
}

Lösung zu Aufgabe 9.):

struct Koordinate {
  int X;
  int Y;
};

struct Koordinate koordinaten[4];

void setup() {
  Serial.begin(9600);
  
  koordinaten[0] = {7, 12};
  koordinaten[1] = {2, 10};
  koordinaten[2] = {15, 1};
  koordinaten[3] = {1, 2};
}
void printKoordinate(struct Koordinate k) {
  Serial.print("X=");
  Serial.print(k.X);
  Serial.print(", Y=");
  Serial.print(k.Y);
}

float distanz(struct Koordinate k1, struct Koordinate k2) {
  return sqrt(sq(k1.X - k2.X) + sq(k1.Y - k2.Y));
}

void loop() {
  for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
      Serial.print("Distanz zwischen dem Punkt");
      printKoordinate(koordinaten[i]);
      Serial.print(" und ");
      printKoordinate(koordinaten[j]);
      Serial.print(" ist ");
      Serial.println(distanz(koordinaten[i], koordinaten[j]));
    }
  }
  Serial.println("");

  delay(1000);
}

Zeiger Grundlagen

Übungsaufgaben

1.) Zeiger 1

int a = 2;
int b = 3;
int *pointer = 0;

pointer = &a;
b = b + *pointer;

Was ist der Wert von b nach Ausführung des Codes?

2.) Zeiger 2

Gegeben sei folgendes Codefragment:

int a = 2;
int b = 3;
int c = 0;
int *pointer1 = 0;
int *pointer2 = 0;

pointer1 = &a;
pointer2 = &b;
c = *pointer1 * b * *pointer2;

Was ist der Wert von c nach Ausführung des Codes?

3.) Zeiger 3

int one = 1;
int two = 2;
int three = 3;
int *my_pointer = 0;
my_pointer = &one;
*my_pointer = two + three;
my_pointer = &two;
*my_pointer = one + three;
my_pointer = &three;

Was ist der Inhalt der Speicherstelle, auf die der Zeiger my_pointer nach Ausführung des Codes zeigt?

4.) Zeiger

Gegeben ist folgendes Programm. Füllen Sie die Lücken [ ]:

void setup() {
  Serial.begin(9600);
  int a = 5;
  int [   ] b;
  b = [   ] a;
  *b = 7;
    Serial.println(a);  // Ausgabe: [   ]
    }

5.) Zeiger

Gegeben ist folgendes Programm. Füllen Sie die Lücken [ ]:

void setup() {
  Serial.begin(9600);
  long a = 5L;
  [   ]*b;
  b = &a;
  *b = 99;
    Serial.println([   ]);  // Ausgabe des Wertes von b: [   ] 
    }

6.) Zeiger

Gegeben ist folgendes Programm. Füllen Sie die Lücken [ ]:

void setup() {
  Serial.begin(9600);
  int a = 5;
  int b = 7;
  int *c = 0;
  int *d = 0;
  int *e = 0;
  c = &a;
  d = &b;
  e = d;

  (*c)++;

  *e = *d*b; a = 99;

  Serial.println(a);  // Ausgabe: [   ]
  Serial.println(b);  // Ausgabe: [   ]
  Serial.println(*c); // Ausgabe: [   ]
  Serial.println(*d); // Ausgabe: [   ]
  Serial.println(*e); // Ausgabe: [   ]

}

7.) Zeiger

Gegeben ist folgendes Programm. Füllen Sie die Lücken [ ]:

struct Person {
  String Name;
  int Alter;
  
};

struct Pointer {
  int *Alter;
};

void setup() {
  Serial.begin(9600);
  struct Person p = {"Max Mustermann", 22};
  struct Pointer ptr;
  ptr.Alter = [   ];
  Serial.println(p.Alter);    // Ausgabe: 22
  Serial.println([   ]ptr.Alter); // Ausgabe: [   ]
}

8.) Arrays und Zeiger

Schreiben Sie ein folgendes Programm:

  • Ein Array enthält die Zahlen von 1 bis 10

  • Ein Array enthält die Pointer auf die Elemente von ersten Array

  • Geben Sie beide Arrays aus

9.) Zeiger

Gegeben ist folgender Codeschnipsel:

int a;
int *b;
int c;
a = 2;
b = &a;
c = *b;

Welcher Wert steht am Ende der Codeausführung in der Variablen?

a) die Adresse von a

b) die Zahl 2

c) die Adresse von b

Lösungen

Lösung zu Aufgabe 8.):

int numbers[10];
int *numbers_ptr[10];

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 10; i++) {
    numbers[i] = i + 1;
    numbers_ptr[i] = &numbers[i];
  }

  // Ausgabe der Werte
  for (int i = 0; i < 10; i++) {
    Serial.print(numbers[i]);
    Serial.print("\t");
    Serial.println(*numbers_ptr[i]);
  }
}

void loop() {
}

Zeigerarithmetik

Übungsaufgaben

1.) Arrays und Zeiger 1

Gegeben sei folgendes Codefragment:

int array = {5,6,7};
int *pointer = 0;
int a = 0;
pointer = array;
pointer++;
a = *pointer;

Was ist der Wert von a nach Ausführung des Codes?

2.) Arrays und Zeiger 2

Gegeben sei folgendes Codefragment:

int array = {5,6,7};
int *pointer = 0;
int *a = 0;
int *b = 0;
pointer = array;
a = pointer++;
b = pointer;
  • Was ist der Wert von a nach Ausführung des Codes?

  • Was ist der Wert von b nach Ausführung des Codes?

Der Operator ++ inkrementiert seinen Operanden erst nachdem sein Wert in a gespeichert wurde. Das heisst, der Wert von pointer wird zuerst in a gespeichert und erst dann inkrementiert. Wenn man andersrum haben möchte, dann muss man entweder a = (pointer++) oder a = ++pointer angeben.

3.) Arrays und Zeiger 3

Gegeben sei folgendes Codefragment:

int array = {7,8,9,10,11};
int *pointer = 0;
pointer = array + 2;
a = *pointer;
pointer = pointer + 2;
b = *pointer;
pointer = pointer + 3;
c = *pointer;

a) Was ist der Wert von a nach Ausführung des Codes? b) Was ist der Wert von b nach Ausführung des Codes? c) Was ist der Wert von c nach Ausführung des Codes?

4.) Zugriff auf die Werte eines Arrays

Geben Sie das 6. Element des Arrays „numbers“ aus. Welche Beispiele sind korrekt?

5.) Größe eines Arrays

Wie können Sie die Größe eines statisch definierten Arrays "numbers" vom Typ int bestimmen?

6.) Iterieren

Sie haben ein Array „numbers“ vom Typ int der Größe NUMBERS_SIZE. Mit welchen der folgenden Schleifen können Sie über die Ergebnisse iterieren?

7.) Zeigerarithmetik

Das folgende Programm soll ein Array mit 2-Byte-Integern auslesen. In jedem Schleifendurchlauf soll ein Element ausgegeben werden.

int liste[5] = {2,3,4,5,6};

int i;

int *liste_ptr;

for (i=0; i<5; i++) {

XXXXX

Serial.println(*liste_ptr);

}

Welche der unten angegebenen Zeilen muss für XXXXX eingesetzt werden?

a) liste_ptr = liste + 2*i;

b) liste_ptr = liste + i;

c) liste_ptr = liste * i;

Array of Arrays / Array of Strings

Übungsaufgaben

Im folgenden wird folgendes Stringarray betrachtet:

char mystringarray[?][?] = {"eins", "zwei", "drei", "vier", "fuenf"};

1.) Dimension eines Arrays

Welche Werte müssen für die Dimensionen des Arrays angegeben werden?

Erster Index: Zweiter Index:

2.) "fuenf"

Vom Element "fuenf" soll der letzte Buchstabe mit folgendem Code ausgelesen werden:

char *sample_ptr;
char sample;
sample_ptr = &mystringarray[0][0] + stringlaenge * zeile + spalte;
sample = *sample_ptr;

Was sind die Werte für stringlaenge, zeile und spalte?

3.) "drei"

Der String "drei" aus dem Array mystringarray soll in den Speicherbereich kopie kopiert werden. Welche Codefragmente erfüllen diese Funktion? a), b), c), d) oder e)?

4.) Array of Arrays

Ein Array ist deklariert als

int my_array[10][10][10];

a) Wie viele Dimensionen hat das Array?

b) Wie viele Elemente hat das Array?

c) Ein Element des Arrays soll ausgelesen werden. Welche der drei folgenden Zeilen ist richtig?

  • a=my_array[[3][5][6]];

  • a=my_array[356];

  • a=my_array[3][5][6];

Funktionen: Pass by Value, Pass by Reference

Übungsaufgaben

1.) Funktionen

Es gibt zwei Funktionen, denen die Zahl „zahl“ übergeben werden soll. Die Funktion „passValue“ erwartet „zahl“ als Wert, die Funktion „passReference“ entsprechend als Referenz. Vervollständigen sie den folgenden Codeausschnitt entsprechend:

int zahl = 5;
passValue([    ]);
passReference([    ])

2.) Pass by Value

Implementieren Sie die Funktion „passValue“. Sie soll die übergebene Zahl „zahl“ um um eins erhöhen und anschließend ausgeben.

3.) Gültigkeit

Welche Zahlenfolge gibt das folgende Programm aus?

void setup() {
  Serial.begin(9600);
  int zahl = 5;
  passValue(zahl);
  Serial.print(zahl);
  passReference(&zahl);
  Serial.print(zahl);
}
void loop() {
  // put your main code here, to run repeatedly:
}
void passValue(int zahl){
  zahl++;
  Serial.print(zahl);
}
void passReference(int *zahl){
  (*zahl)--;
  Serial.print(*zahl);
}

4.) Arrays und Referenzen 1

Schreiben Sie eine Funktion, die immer den ersten Wert eines übergebenen Arrays ausgibt.

5.) Arrays und Referenzen 2

Wie rufen Sie die Funktion auf, wenn sie die erste Position des Arrays int werte[2]; ausgeben möchten?

6.) Arrays und Referenzen 3

Nun möchten Sie die zweite Position des Arrays ausgeben, jedoch ohne die Funktion printValue zu verändern. Welche zwei Möglichkeiten haben Sie die Referenz entsprechend zu übergeben?

7.) Summe

Schreiben Sie eine Funktion, die die Summe über ein Array (int) erzeugt. Diese Funktion hat zwei Parameter: Das Array sowie die Zahl der Felder, über die die Summe berechnet werden soll. Die Funktion gibt die Summe (int) zurück.

8.) Komplette Summe

Schreiben Sie ein Programm, welches die Summe eines Array berechnet. Das Array enthält folgende Zahlen: 1, 5, 4, 90. Nutzen Sie die Funktion der vorherigen Aufgabe.

9.) Funktionen und Zeiger

Gegeben sei folgender Codeausschnitt

void function_1 (int *a) {

*a=*a * 2;

}

void function_2 (int a) {

a= a * 2;

}

int b = 3;

function_1(&b);

function_2(b);

Was ist der Wert von b nach Ausführung des Codes?

Lösungen

Lösung zu Aufgabe 2.):

void passValue(int zahl){
  zahl++;
  Serial.println(zahl);
}

Lösung zu Aufgabe 3.):

6544

Lösung zu Aufgabe 4.):

void printValue(int *a){
  Serial.println(a[0]); // oder
  Serial.println(*a);
}

Lösung zu Aufgabe 5.):

printValue(werte);

Lösung zu Aufgabe 6.):

printValue(&werte[1]);
printValue(werte+1);

Lösung zu Aufgabe 7.):

int summe(int *werte, int num){
  int s = 0;
  for (int i = 0; i<num; i++){
    s+=werte[i];
  }
return s; }

Lösung zu Aufgabe 8.):

int summe(int *werte, int num){
  int s = 0;
  for (int i = 0; i<num; i++){
    s+=werte[i];
  }
return s; }


void setup() {
  Serial.begin(9600); 
  int numbers[4]={1,5,4,90};
  int sum=summe(numbers,4);
    
  Serial.print(sum);

}

Stack mit Zeigern

Im Video wurde die Grundfunktion eines Stacks erklärt. Im Beispiel fehlte jedoch die Fehlerbehandlung.

Übungsaufgaben

1.) Fehler bei push()

Welcher Fehler kann im Falle der Funktion push() auftreten?

  • Der Stack ist voll, es können keine Werte gespeichert werden.

  • Der Stack ist voll, weitere Werte können im Überlauf gespeichert werden.

  • Der Stack ist leer: Es können keine gültigen Werte zurück gegeben werden

  • Der Stack ist leer: Es wird der jeweils letzte Wert zurück gegeben

2.) Fehler bei pop()

Welcher Fehler kann im Falle der Funktion pop() auftreten?

  • Der Stack ist voll, es können keine Werte gespeichert werden.

  • Der Stack ist voll, weitere Werte können im Überlauf gespeichert werden.

  • Der Stack ist leer: Es können keine gültigen Werte zurück gegeben werden

  • Der Stack ist leer: Es wird der jeweils letzte Wert zurück gegeben

3.) Implementation des Stacks

Implementieren Sie den Stack wie im Video gezeigt. Behandeln Sie dabei die möglichen Fehler für push() und pop(). Im Erfolgsfall sollen die Funktionen Wahr (true), im Fehlerfall Falsch (false) zurück geben.

4.) Test des Stacks

Nutzen Sie die loop()-Funktion, um Ihre Implementierung zu testen. Achten Sie dabei besonders auf die Fehlerfälle.

5.) Top

Im Video wurde besprochen, dass es auch eine Funktion geben kann, die den obersten Wert zurück gibt, ohne diesen vom Stack zu entfernen. Implementieren Sie diese Funktion.

Lösungen

Lösung zu Aufgabe 3.):

const short STACK_SIZE = 10;

int stack[STACK_SIZE];
int *head = stack;

bool push(int val) {
  if (head < stack + STACK_SIZE) {
    *head = val;
    head++;
    return true;
  } else {
    // Stack full
    return false;
  }
}

bool pop(int *val) {
  if (head != stack) {
    head--;
    *val = *head;
    return true;
  } else {
    // Stack empty
    return false;
  }
}

Lösung zu Aufgabe 4.):

const short STACK_SIZE = 10;

int stack[STACK_SIZE];
int *head = stack;

bool push(int val) {
  if (head < stack + STACK_SIZE) {
    *head = val;
    head++;
    return true;
  } else {
    // Stack full
    return false;
  }
}

bool pop(int *val) {
  if (head != stack) {
    head--;
    *val = *head;
    return true;
  } else {
    // Stack empty
    return false;
  }
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < STACK_SIZE + 1; i++) {
    Serial.print("Hinzufuegen ");
    Serial.print(i);
    Serial.print(": ");
    if (push(i)) {
      Serial.println("Erfolg");
    } else {
      Serial.println("Fehler");
    }
  }
  for (int i = 0; i < STACK_SIZE + 1; i++) {
    Serial.print("Hole ");
    int number;
    if (pop(&number)) {
      Serial.println(number);
    } else {
      Serial.println("Fehler");
    }
  }
  delay(5000);
}

Lösung zu Aufgabe 5.):

bool top(int *val){
  if (head != stack){
    *val = *(head-1);
    return true;
  } else {
    // Stack empty
    return false;
  }
}

6.) Stack mit Zeigern

Gegeben sei das array int stack[20] und der Stackzeiger int *stack_ptr. a) Bei der Initialisierung des Stackzeigers soll dieser auf den Anfang des Arrays (das Element mit derniedrigsten Speicheradresse) zeigen. Welche der folgenden Codezeilen kann hierfür verwendet werden? (mehrere Antworten möglich).

  • stack_ptr = stack;

  • stack_ptr = stack[0];

  • stack_ptr = &(stack[0]);

  • stack_ptr = stack+20;c

b) Es soll eine pop-Funktion implementiert werden, mit der ein Element aus dem Stack entfernt wird. Wie muss stack_ptr aktualisiert werden, vorausgesetzt, es befinden sich noch Elemente im Stack? (mehrere Antworten möglich)

  • stack_ptr++;

  • stack_ptr--;

  • stack_ptr = stack[stack_ptr-1];

  • stack_ptr = stack_ptr – 1;

Liste (Queue) mit Zeigern

Übungsaufgaben

1.) Warteschlange

Im Video haben Sie die Funktionsweise einer Queue (Warteschlange) kennen gelernt sowie eine Beispielimplementierung erhalten. Warteschlangen werden in der Informatik häufig dort verwendet, wo Ein- und Ausgabe von Daten voneinander entkoppelt sein müssen. Wir nehmen an, dass Sie Temperaturmesswerte ereignisgesteuert als Struct erhalten:

struct messwert {
  unsigned long zeit;
  
  int messwert;
};

Passen Sie die Queue so an, dass dort Werte vom Typ struct messwert gespeichert werden. Testen Sie die Queue anschließend auf ihre Funktionalität. Implementieren und verwenden Sie folgende Hilfsfunktionen:

getMesswert() gibt einen Messwert als Struct zurück:

struct messwert getMesswert()

print_messwert() gibt den Inhalt eines Structs vom Typ messwert zurück:

void print_messwert(struct messwert m)

2.) Queue - Übung

Warteschlangen werden nicht nur zum Zwischenspeichern von Messwerten, sondern auch z.B. zum Verwalten von Druckaufträgen auf einem großen Abteilungsdrucker verwendet. Diesem ist ein Rechner (Print-Server genannt) vorgeschaltet, der Auträge entgegenimmt und in einer Queue speichert. Wenn der Drucker an den Server meldet, dass ein Auftrag abgeschlossen wurde, schickt der Server den nächsten Auftrag an den Drucker. Die Druckerwarteschlange kann nur eine begrenzte Zahl an Aufträgen speichern.Es gibt zwei Funktionen für die Verwaltung der Warteschlange, die jeweils am Anfang der Queue die Auftrangsannahme und am Ende die Weitergabe an den Drucker freigeben oder sperren.Wie sollten diese Funktionen in den folgenden Fällen eingesetzt werden? a) Die Warteschlange ist voll. b) Der Drucker signalisiert an den Printserver, dass kein Papier mehr vorhanden ist – es wird davon ausgegangen, dass kurzfristig Paper nachgelegt werden kann. c) Der Drucker wird in Kürze für Wartungsarbeiten abgeschaltet, bereits in der Queue befindiche Aufträge sollen aber noch abgearbeitet werden.

3.) Queue - Übung

Vergleichen Sie die Eigenschaften von Stacks und Warteschlangen. a) Wieviel Speicherplatz wird jeweils für das Speichern von n Elementen benötigt? b) In welcher Reihenfolge werden die Elemente entnommen, bezogen auf die Reihenfolge des Einschreibens in den Stack oder die Queue? c) Wie viele Zeiger werden für die Implementierung benötigt? Unter welchen Bedingungen und auf welche Werte werden die Zeiger erhöht bzw. erniedrigt?

4.) Warteschlangen

Ergänzen Sie den Lückentext:

Für die Implementierung von Warteschlangen benötigt man zwei Zeiger, die auf _____ und _____ derWarteschlange zeigen. Sind beide Zeiger gleich, ist die Warteschlange ______.

Lösungen

Lösung zu Aufgabe 1.):

struct messwert {
  unsigned long zeit;
  int messwert;
};

const unsigned short QUEUE_SIZE = 10;
struct messwert queue[QUEUE_SIZE];
struct messwert *head; // Kopf
struct messwert *tail; // Ende
bool q_empty;

struct messwert getMesswert() {
  struct messwert m;
  // Messwerte aufnehmen dauert ein wenig...
  delay(random(0, 100));
  m.zeit = millis();
  m.messwert = random(0, 10);
  return m;
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));
  head = queue;
  tail = queue;
  q_empty = true;
}

bool push(struct messwert value) {
  if ((head != tail) || q_empty) {
    *head = value;
    head++;
    if (head >= queue + QUEUE_SIZE) {
      head = queue;
    }

    q_empty = false;
    return true;
  } else {
    // Stack voll
    return false;
  }
}

bool pop(struct messwert *value) {
  if (!q_empty) {
    *value = *tail;
    tail++;
    if (tail >= queue + QUEUE_SIZE) {
      tail = queue;
    }
    if (tail == head) {
      q_empty = true;
    }
    return true;
  }
  else {
    // Stack leer
    return false;
  }
}

void print_messwert(struct messwert m) {
  Serial.print("Zeit: ");
  Serial.print(m.zeit);
  Serial.print(" Wert: ");
  Serial.println(m.messwert);
}
void print_queue() {
  if (q_empty) {
    Serial.println("Die Schlange ist gerade leer");
    return;
  }
  
  struct messwert *i = head - 1;
  if (i < queue)
    i = queue + QUEUE_SIZE - 1;
  while (i != tail) {
    print_messwert(*i);
    i--;
    if (i < queue)
      i = queue + QUEUE_SIZE - 1;
  }
  print_messwert(*i);
}

void loop() {
  
  // Werte hinzufügen
  for (int i = 0; i < QUEUE_SIZE + 2; i++) {
    if (!push(getMesswert())) {
      Serial.println("Fehler bein Hinzufügen"); // Sollte 2x angezeigt werden.
    }
  }
  Serial.println("Nach hinzufügen:");
  print_queue();
  
  // Alle Werte auslesen
  Serial.println("Auslesen & löschen:");
  struct messwert m;
  while (pop(&m)) {
    print_messwert(m);
  }
  
  Serial.println("Warten...");
  
  delay(20000);
}

Fortgeschrittene Aufgaben

Bei der Auswertung eine Reihe von Messungen ist man oft weniger an den einzelnen Werten interessiert, sondern an den statistischen Eigenschaften der Messwerte. Wetterdienste bestimmen beispielsweise die Durchschnitts-, Höchst- und Tiefsttemperaturen innerhalb eines Tages oder Jahres. Verwenden Sie als Grundlage für die Hackathon-Vorbereitung den Code, den Sie im Selbsttest zum Thema Warteschlangen geschrieben haben. Erstellen Sie Funktionen, mit denen die in der Queue gespeicherten Werte untersucht werden können. Nehmen Sie an, dass einmal pro Stunde ein Messwert aufgenommen wird und die Queue die Messwerte für eine Woche (7x24 Messungen) speichert.

  1. Erstellen Sie Funktionen, die das Maximum und das Minimum bestimmen und zusammen mit Datum und Uhrzeit zurückgeben.

  2. Erstellen Sie eine Funktion, die den Durchschnitt berechnet.

  3. Erweitern Sie die Funktionen aus den Punkten 1 und 2 so, dass die dort erwähnten Statistikwerte für einen der letzten 6 vergangenen Tage ausgegeben werden können. Der Beonbachtungszeitraum ist in diesem Fall 0-24 Uhr des betreffenden Tages. Sie können z.B. der Funktion einen Parameter übergeben, bei dem 1 gestern bedeutet, 2 vorgestern usw. Ist der Parameter 0, ist die ganze Woche der Beobachtungszeitraum.

  4. Erstellen Sie Funktionen, die den Tag mit der höchsten oder niedrigsten Durchschnittstemperatur bestimmen und diese Temperatur zusammen mit dem Datum zurückgeben.

  5. Erstellen Sie eine zweite Warteschlange, die parallel zur ersten arbeitet und die Tages-Maximal-. Minimal- und Durchschnittswerte für einen Monat speichert. Am Ende eines jeden Tages (nach der letzten Messung um 23:00h) werden die betreffenden Werte berechnet und in die zweite Queue geschrieben. Zum Testen des Programms können Sie z.B. den Zufallswert eines offenen Analogeingans in kurzen zeitlichen Abständen einlesen.

Zusatz: Unix Epoche

Übungsaufgaben

Hinweis: Die Unix Epoche nutzt die Zahl der Sekunden seit dem 1.1.1970 als 32 oder 64 bit Integer -Wert (long / long long). Da Arduinos in der Regel keine Echtzeituhr besitzen sowie in Millisekunden rechnen, gibt es folgende Einschränkungen:

  • Die Zeit wird bei jedem Neustart zurück gesetzt

  • Die Zeit wird durch die Funktion millis() in Millisekunden seit Start zurück gegeben.

  • Der Dateityp ist hier unsigned long.

Unabhängig davon lässt sich mit diesen Zeiten ähnlich wie mit dem Unix Timestamp rechnen.

50.) 1 Tag

Welchen Wert gibt millis() nach einem Tag zurück?

51.) Überlauf

Die Zeit im Arduino wird in Millisekunden seit Systemstart in einer Variable vom Typ unsigned long gespeichert. Nach wievielen Tagen kommt es hier zu einem Überlauf?

Lösungen

Lösung zu Aufgabe 50:

1000*60*60*24=86400000

Lösung zu Aufgabe 51:

unsigned long entspricht 32 bit, also maximal 232=4294967296 ms. Dies entspricht 49,7 Tagen.

Zusatz: Ringpuffer

Übungen

1.) Theorie Ringpuffer 1

In einen Ringpuffer mit 3 Speicherplätzen, die beginnend mit 0 nummeriert sind, werden Werte geschrieben. Der Schreibvorgang beginnt mit Speicherplatz 0. Die Werte 9, 7, 2, 8, 5 werden geschrieben.

  • Was ist der Inhalt des Speicherplatzes 0, nachdem alle Werte geschrieben wurden?

  • Was ist der Inhalt des Speicherplatzes 1, nachdem alle Werte geschrieben wurden?

  • Was ist der Inhalt des Speicherplatzes 2, nachdem alle Werte geschrieben wurden?

2.) Theorie Ringpuffer 2

Ausgehend von der vorherigen Übung. Der Schreibvorgang wird mit den Werten 3, 1, 4, 0, 6 fortgesetzt. Der Ringpuffer befindet sich vor Beginn des Schreibvorgangs im Zustand nach Beendigung von Aufgabe 1.

  • Was ist der Inhalt des Speicherplatzes 0, nachdem alle Werte geschrieben wurden?

  • Was ist der Inhalt des Speicherplatzes 1, nachdem alle Werte geschrieben wurden?

  • Was ist der Inhalt des Speicherplatzes 2, nachdem alle Werte geschrieben wurden?

3.) Ausgabe Puffer

Wir haben einen Puffer / Buffer mit dem Namen times vom Typ unsigned long der Länge BUFFER_SIZE. Schreiben Sie eine Funktion printBuffer(), welche die Werte des Buffers sowie die Position des Wertes im Buffer ausgibt. times und BUFFER_SIZE sind hierbei global definiert.

4.) Speichern von Werten

Speichern Sie den aktuellen Wert von millis() ein mal pro Sekunde in diesem Buffer. Überschreiben hierbei den jeweils ältesten Wert. Geben Sie die Werte von times regelmäßig aus.

Lösungen

Lösung zu Aufgabe 3.):

void printBuffer(){
  Serial.println("####");
  for (int i=0; i<BUFFER_SIZE; i++){
    Serial.print(i+1);
    Serial.print(": ");
    Serial.println(times[i]);
  }
  Serial.println("****");
}

Lösung zu Aufgabe 4.):

const unsigned int BUFFER_SIZE = 7;

unsigned long times[BUFFER_SIZE];

void setup() {
  Serial.begin(9600);
}

void printBuffer() {
  Serial.println("####");
  for (int i = 0; i < BUFFER_SIZE; i++) {
    Serial.print(i + 1);
    Serial.print(": ");
    Serial.println(times[i]);
  }
  Serial.println("****");
}

unsigned int counter = 0;
void loop() {
  times[counter % BUFFER_SIZE] = millis();
  counter++;
  printBuffer();
  delay(1000);
}

Last updated