C Arrays (Felder)

Array

Array

eine Sammlung von Elementen des gleichen Datentyps,
die unter einem gemeinsamen (Variable) Namen gespeichert sind

die Elemente sind im Speicher sequenziell angeordnet

hat eine Größe (positive Ganzzahl / Ganzzahl-Konstante), die zur Laufzeit nicht geändert werden kann

int counter[10];  

const int LAENGE = 128;  
char text[LAENGE];  

jedes Element kann über seinen Index (die Position) direkt angesprochen werden

der Index beginnt bei 0 und endet bei Größe n-1!

C führt keine automatische Überprüfung der Array-Grenzen durch (erzeugt auch kein Compile-Error). Zugriff auf einen Index außerhalb des deklarierten Bereichs (beim Überschreiten / Unterschreiten [negativer Index]) führt zu undefiniertem Verhalten (Buffer Overflow)!

Strings (Zeichenketten) in C,
Arrays von Typ char, die mit Null-Terminator \0 enden

Initalisierung

nach der Deklaration

einzeln Werte über Index zuweisen ...

int elemente[3];  
elemente[0] = 12345;  
elemente[1] = 666;  
elemente[2] = 7;  

oder mit Schleife, z.B.

int liste[1000];  
for(int i = 0; i < 1000; i++) liste[i] = 0;  

während der Deklaration

jedes Feld mit bestimmten Wert initalisieren

int zahlen[5] = { 10, 20, 30, 40, 50 };  

einzelne Felder mit bestimmten Wert initalisieren (Rest wird mit 0 initalisiert)

int zahlen[5] = { 10, 20 };        // ==> { 10, 20, 0, 0, 0 }  

einzelne / gezielt Felder mit bestimmten Wert initalisieren

int zahlen[5] = { 10, [3] = 123 }; // ==> { 10, 0, 0, 123, 0 }  

vollständig mit 0 initalisieren

int zahlen[5] = { 0 };  

ohne Angabe der Größe wird die Größe automatisch an die Anzahl der Initialisierungswerte angepasst

int zahlen[] = { 10, 20, 30 }; // ==> groesse = 3  

Arrays mit dynamischer Größer (also anhand definierter Variable) ergibt Compile-Error!

int ANZAHL = 3;  
int zahlen[ANZAHL] = { 0 }

Lösung 1: nach Deklaration mit Schleife initialisieren

int ANZAHL = 3;  
int zahlen[ANZAHL];  
for(int i = 0; i < ANZAHL; i++) zahlen[i] = 0;

Lösung 2: über definiterer Makro-Konstante ...

#define LAENGE 3  

int main() {  
    int zahlen[LAENGE] = { 0 };  
    return 0;  
}

Beispiel

#include <stdio.h>  

int main() {  
    float noten[5] = { 6, 8.5, 7.75, 4.25, 10 };  

    printf("1. Note ist: %.2f\n", noten[0]); // ausgabe: 6.00  
    printf("3. Note ist: %.2f\n", noten[2]); // ausgabe: 7.75  

    noten[1] = 9.5; // 2. note wird von 8.5 auf 9.5 geaendert  
    printf("Die geaenderte 2. Note ist: %.2f\n", noten[1]); // ausgabe: 9.50  

    // iterieren durch das array mit einer schleife  
    printf("Alle Noten im Array:\n");  
    for (int i = 0; i < 5; i++) {  
        printf("Note an Index %d: %.2f\n", i, noten[i]);  
    }  

    // array von zeichen (zeichenkette / string)  
    char name[] = "Alice";  
    printf("Name: %s\n", name); // ausgabe: Alice  
    printf("1. Buchstabe des Namens: %c\n", name[0]); // ausgabe: A  

    return 0;  
}  

Größe berechnen

die Größe des Arrays kann mit Hilfe der sizeof-Funktion berechnet werden

int zahlen[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21};  
printf("Array 'zahlen' hat %d Elemente\n", sizeof(zahlen) / sizeof(int));  

an Funktionen übergeben

Arrays werden bei der Übergabe an Funktionen nicht selbst übergeben (kopiert),
sondern nur die Anfangsadresse des Arrays

Änderungen an einem Array in einer Funktion sind auch außerhab der Funktion sichtbar!

Übungen (eindimensional Arrays):

📋 Statisik Zufallszahlen

Schreibe ein Programm mit einem Array der Größe g, welches mit Zufallszahlen von 1-10 gefüllt wird und anschließend eine Statistik erstellt, welche Zahlen wie oft im Array vorhanden sind.

📋 min / max / Position

Schreibe ein Programm welches ein Array der Länge l mit Zufallszahlen füllt.

Ermittelt werden soll:

Erweitere das Programm um eine Benutzereingabe, für die zusätzliche Möglichkeit, den Bereich (von-bis) für die Zufallszahlen festlegen / eingrenzen zu können.

📋 Mengenoperationen

Gegeben sind zwei Arrays A und B (mit fixer Länge).

Programmiere eine Überprüfung, ob die zwei Arrays gleich sind,
d.h. ob an jeder Position der Arrays derselbe Wert steht.

zwei Arrays sollten nicht mit dem Operator == verglichen werden! damit werden nur die Speicheradressen und nicht der Inhalt der Arrays verglichen!

Programmiere die folgenden Mengenoperationen und zeige das Resultat bei der Programmausführung an.

Vereinigungsmenge von A und B

A = { 2, 34, 5,  6,  3, 17 }  
B = { 2,  4, 3, 46, 39, 17 }  
A ∪  B = { 2, 34, 5, 6, 3, 17, 4, 46, 39 }  

Schnittmenge von A und B

A = { 2, 34, 5,  6,  3, 17 }  
B = { 2,  4, 3, 46, 39, 17 }  
A ∩ B = { 2, 3, 17 }  

Differenzmenge von A und B, d.h. Elemente aus A, die nicht in B sind

A = { 2, 34, 5,  6,  3, 17 }  
B = { 2,  4, 3, 46, 39, 17 }  
A \ B = { 34, 5, 6 }  


📋 fehlende Zahl

Finde die fehlende Zahl in einer Zahlenfolge von 1 bis n.
Das Array enthält n-1 verschiedene Zahlen von 1 bis n.

📋 Peak Element

Finde ein Peak Element / Spitzenelement (ein Element, größer oder gleich seiner Nachbarn) in einem Array. Wenn mehrere Spitzenelemente vorhanden sind, ist jedes beliebige Element in Ordnung. Beachte, dass Randelemente nur einen Nachbarn haben.

Array = { 1, 2, 3, 1 }          => Peak = 3  
Array = { 1, 2, 1, 3, 5, 6, 4 } => Peak = 2, 6  


📋 längste zusammenhängende Teil-Array

Finde das längste zusammenhängende Teil-Array (Subarray) mit einer bestimmten Summe.

Array = { 1, 4, 20, 3, 10, 5 }, Summe = 33  
=> Teil-Array ab Position 2 bis 4 (20, 3, 10)  


📋 longest increasing subsequence (LIS)

Finde die Länge der längsten aufsteigenden Teilfolge (longest increasing subsequence / LIS).

Array = { 10, 22, 9, 33, 21, 50, 41, 60 }  
=> LIS length = 5 ({ 10, 22, 33, 50, 60 })  


📋 Triplet bestimmter Summe

Finde das Triplet (drei Elemente) in einem Array, dessen Summe einem vorgegebenen Zielwert am nächsten kommt. Gehe davon aus, dass das Array mindestens drei Elemente enthält.

Array = { -1, 2, 1, -4 }, Zielwert = 1  
=> Summe des Triplets = 2 ({ -1, 2, 1 })  


mehrdimensionale Arrays

Arrays von Arrays (ein Array, dessen Elemente selbst Arrays sind)

für Darstellung von Daten, die eine natürliche Struktur in mehreren Dimensionen haben

die Elemente sind linear (an aufeinanderfolgenden Adressen) im Speicher abgelegt

2D-Array (z.B. Tabelle / Matrix)
mit 3 Zeilen die jeweils 4 Spalten haben (insgesamt 12 int-Elemente)

int matrix[3][4];  

3D-Array (z.B. Würfel)
mit 3 Zeilen pro Schicht und 4 Spalten pro Zeile (insgesamt 24 float-Elemente)

float cube[2][3][4];  

Initialisierung

explizite Initialisierung

int matrix[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };  
// entspricht:  
// matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;  
// matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;  

automatische Größenbestimmung

die erste Dimension kann weggelassen werden, wenn alle Elemente initialisiert werden

int matrix[][3] = { { 10, 20, 30 }, { 40, 50, 60 }, { 70, 80, 90 } };  
// compiler bestimmt, dass es 3 zeilen gibt  

partielle Initialisierung

nicht initialisierte Elemente werden mit 0 belegt (für numerische Typen)

int matrix[2][3] = { { 1 }, { 4, 5 } };  
// matrix wird zu: { { 1, 0, 0 }, { 4, 5, 0 }}  

3D-Array Initialisierung

int cube[2][2][2] = {  
    { { 1, 2 }, { 3, 4 } }, // erste schicht  
    { { 5, 6 }, { 7, 8 } }  // zweite schicht  
};  

Beispiel: 2D-Array

#include <stdio.h>  

int main() {  
    // deklaration und initialisierung eines 2x3 integer-arrays  
    int matrix[2][3] = {  
        { 1, 2, 3 },  
        { 4, 5, 6 }  
    };  

    printf("2D-Array (Matrix):\n");  

    // iteration und ausgabe der elemente  
    for (int i = 0; i < 2; i++) {       // zeilen  
        for (int j = 0; j < 3; j++) {   // spalten  
            printf("%d ", matrix[i][j]);  
        }  
        printf("\n");  
    }  

    // zugriff auf ein spezifisches element  
    printf("Element in Reihe 0, Spalte 1: %d\n", matrix[0][1]); // >> 2  

    // modifizieren eines elements  
    matrix[0][1] = 99;  
    printf("Modifiziertes Element in Reihe 0, Spalte 1: %d\n", matrix[0][1]); // >> 99  

    return 0;  
}  

Beispiel: 3D-Array

#include <stdio.h>  

int main() {  
    // deklaration und initialisierung eines 2x2x2 integer-arrays  
    int cube[2][2][2] = {  
        { { 1, 2 }, { 3, 4 } },  // erste schicht  
        { { 5, 6 }, { 7, 8 } }   // zweite schicht  
    };  

    printf("3D-Array (Cube):\n");  

    // iteration und ausgabe der elemente  
    for (int i = 0; i < 2; i++) {           // schichten  
        printf("Schicht %d:\n", i);  
        for (int j = 0; j < 2; j++) {       // zeilen  
            for (int k = 0; k < 2; k++) {   // spalten  
                printf("%d ", cube[i][j][k]);  
            }  
            printf("\n"); // neue zeile nach jeder zeile  
        }  
        printf("\n");     // leerzeile zwischen den schichten  
    }  

    // zugriff auf ein spezifisches element  
    printf("Element in Schicht 1, Reihe 0, Spalte 1: %d\n", cube[1][0][1]); // >> 6  

    // modifizieren eines elements  
    cube[0][1][0] = 100;  
    printf("Modifiziertes Element in Schicht 0, Reihe 1, Spalte 0: %d\n", cube[0][1][0]); // >> 100  

    return 0;  
}  

Übergabe an Funktionen

der Compiler muss die Dimensionen kennen, um die Adressberechnung korrekt durchzuführen

alle Dimensionen außer der ersten müssen in der Funktionssignatur angegeben werden

void printMatrix(int arr[][3], int rows, int cols); // ein 2D-array mit 3 spalten  
void printCube(int arr[][2][2], int dim1, int dim2, int dim3); // ein 3D-array  

Übungen (mehrdimensional):

📋 Tic-Tac-Toe

Auf einem quadratischen, 3×3 Felder großen Spielfeld setzen zwei Spieler abwechselnd ihr Zeichen (ein Spieler Kreuze X, der andere Kreise O) in ein freies Feld. Der Spieler, der als Erster drei Zeichen in eine Zeile, Spalte oder Diagonale setzen kann, gewinnt.

Erweiterungen:

📋 Kalender

Für das Jahr 2022 soll ein Kalender erzeugt, befüllt und ausgegeben werden, wie in der Beispiel-Ausgabe zu sehen ist.

Nutze zur Abbildung ein dreidimensionales Array: [MONATE][WOCHEN][TAGE]

Jaenner:  
KW| Mo Di Mi Do Fr Sa So  
 1|                 1  2  
 2|  3  4  5  6  7  8  9  
 3| 10 11 12 13 14 15 16  
 4| 17 18 19 20 21 22 23  
 5| 24 25 26 27 28 29 30  
 6| 31  

Februar:  
KW| Mo Di Mi Do Fr Sa So  
 6|     1  2  3  4  5  6  
 7|  7  8  9 10 11 12 13  
 8| 14 15 16 17 18 19 20  
 9| 21 22 23 24 25 26 27  
10| 28   

Erweiterung:

weitere Übungen

📋 Umwandlung 2D zu 1D

Schreiben ein Programm, das ein zweidimensionales Array der Größe R x C in ein eindimensionales Array der Größe R * C umwandelt.

📋 Zahlen-Schnecke

Kann für eine bestimmte Zahl eine Zahlen-Schnecke (wie auf einem Brettspiel) geformt werden?
Falls ja, soll die Form in der Konsole ausgeben werden.

Das Feld ist n * n groß und die Zahl 0 kann, je nachdem ob sie benötigt wird, eingefügt oder weggelassen werden.

2   => Fehler: 2 ist zu klein fuer eine Zahlenschnecke  

4   =>   1   2  
         4   3  

19  =>   0   1   2   3   4  
        13  14  15  16   5  
        12  19  18  17   6  
        11  10   9   8   7  

Erweiterungen:

📋 Vier gewinnt

Das Spiel wird auf einem senkrecht stehenden hohlen Spielbrett gespielt, in das die Spieler abwechselnd ihre Spielsteine fallen lassen. Das Spielbrett besteht aus 7 Spalten (senkrecht) und 6 Reihen (waagerecht). Jeder Spieler besitzt 21 gleichfarbige Spielsteine. Wenn ein Spieler einen Spielstein in eine Spalte fallen lässt, besetzt dieser den untersten freien Platz der Spalte.

Gewinner ist der Spieler, der es als erster schafft, vier oder mehr seiner Spielsteine waagerecht, senkrecht oder diagonal in eine Linie zu bringen.

Das Spiel endet unentschieden, wenn das Spielbrett komplett gefüllt ist, ohne dass ein Spieler eine Viererlinie gebildet hat. Sollte ein Spieler das Spiel verlieren, aber alle seine Spielsteine sind miteinander verbunden, so erhält er einen Zusatzpunkt und das Spiel endet Unentschieden.

📋 Schiffe versenken

Der Computer platziert auf einem 10x10 Kästchen großen Spielfeld mehrere Schiffe (ein 4er-Schiff das 4 Kästchen groß ist, zwei 3er-Schiffe und drei 2er-Schiffe). Die Schiffe werden zufällig platziert (vertikal oder horizontal), jedoch so, dass sich zwei Schiffe nicht berühren. Es muss mindestens ein Kästchen zwischen zwei Schiffen frei bleiben.

Der Benutzer kann durch die Eingabe von Koordinaten raten, wo sich die Schiffe befinden könnten, welche dann folgendermaßen bewertet: "Wasser \/ knapp daneben", "Treffer", "Treffer, Schiff versenkt". Neben dieser Information soll nach jedem Raten ein aktualisiertes Spielfeld angezeigt werden, in dem die schon geratenen Positionen, getroffene Schiffe, etc. geeignet dargestellt werden.

📋 Punkte sammeln

Eine Spielfigur soll auf einem Spielbrett bewegt werden können. Das Spielfeld wird in einem Array gespeichert. Die Größe des Feldes wird vom Benutzer gewählt. Die Startposition der Figur ist in der Mitte. Mit den Tasten w, a, s, d kann man die Figur auf dem Feld bewegen. Wird die Spielfeldgrenze überschritten, landet die Figur auf der gegenüberliegenden Seite. Das Programm lässt sich mit Taste x beenden. Auf dem Spielfeld erscheinen immer wieder zufällig Symbole: Zs und Os. Ziel ist es, mit der Figur die Os einzusammeln und die Zs nicht zu berühren. Für jedes O gibt es 10 Punkte, läuft
die Figur auf ein Z auf, verliert sie ein Leben. Die Figur hat insgesamt drei Leben. Während die Os beim Auflaufen wieder verschwinden, bleiben die Zs auf dem Spielfeld immer erhalten.

Befehl um die Konsole zu leeren / löschen:

system("cls");   // unter windows  

system("clear"); // unter linux

Befehl um eine bestimmte Zeit lang zu warten / pausieren

#include <unistd.h> // standard symbolic constants and types  

sleep(1);    // in sekunden  

usleep(100); // in micro sekunden  

system("pause"); // pausieren bis taste gedrueckt wird

Befehle um einzelne Taste einzulesen (ohne zusätzliches Betätigen der ENTER-Taste)

#include <conio.h> // fuer getch()  

char key;  
while(key != 'x'){  
    key = getch();   
    printf("Eingabe: %c (ASCII: %d)\n", key, key);  
}