Algorithmen und Datenstrukturen in C/ Felder

Ein Feld (oder Array) ist eine Datenstruktur, die mehrere Elemente eines Datentyps fortlaufend im Speicher anordnet. Die Größe des Feldes ist beliebig wählbar, muss jedoch zum Übersetzungszeitpunkt feststehen. Eine Änderung der Größe zur Laufzeit ist nicht möglich. Als Datentyp kann jeder in C gültige Typ benutzt werden, auch Strukturen. Zu unterscheiden ist außerdem zwischen ein- und mehrdimensionalen Feldern.

Wikipedia hat einen Artikel zum Thema:

Auch bei dynamisch erzeuten Feldern kann die Feldgröße nach der Erstellung nicht mehr geändert werden. Hier müsste ein neues, größeres Feld angelegt, die Elemente umkopiert und der Speicher des alten Feldes freigegeben werden.

Der Zugriff auf ein Element eines Feldes erfolgt über einen ganzzahligen Index. Das erste Element hat den Index 0! Für ein Feld mit 100 Elementen darf der Index also die Werte von 0 bis 99 annehmen! Vorsicht! Indexfehler bei Feldzugriffen kommen häufig vor und werden vom C-Compiler nicht beanstandet! Wenn man eine Operation außerhalb der Feldgröße ausführt, kann es zu Speicherfehlern und zum Programmabsturz kommen.

Vorteile

  • Zugriffe auf Elemente eines Arrays erfolgen in konstanter Laufzeit.
  • Ungeordneter Zugriff auf Elemente eines Arrays ist einfach und effizient möglich.

Nachteile

  • Die Größe eines Feldes lässt sich zur Laufzeit nicht mehr verändern.

Eindimensionale Felder Bearbeiten

Das folgende Beispiel zeigt, wie man ein eindimensionales Feld aufbaut und ausgibt:

#include <stdio.h>

#define ARR_SIZE 10
 
void printArray(int* array, int length)
{
    int i;
    
    // Feld auslesen
    for (i = 0; i < length; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
}
 
int main(void)
{
    int array[ARR_SIZE];
    int i;

    // Feld mit Zahlen füllen
    for (i = 0; i < ARR_SIZE; i++)
    {
        array[i] = i;
    }

    printArray(array, ARR_SIZE);
    return 0;
}

In der Hauptfunktion wird das Feld array[] vom Datentyp Integer und einer Größe von zehn Elementen erstellt. Das Feld wird mit den Zahlen 0 bis 9 gefüllt. Anschließend wird das Feld durch die Funktion printArray() ausgegeben:

0 1 2 3 4 5 6 7 8 9

In diesem Beispiel entspricht die Zahl in jedem Element seinem Index. Der Index i eines Feldes der Größe N ist immer 0 <= i < N bzw. 0 <= i <= (N - 1).


Algorithmen Bearbeiten

Einfügen Bearbeiten

Um in ein Feld, das bereits teilweise fortlaufend mit Daten gefüllt ist, an einer Position ein Datum einzufügen, müssen alle Elemente rechts von der Einfügeposition um eins nach rechts geschoben werden:

0 1 2 4 5 6 7 8 9
      ^ Einfügeposition i=3 für Zahl 3
0 1 2   4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

Alternativ kann man natürlich auch an das Ende einfügen. Folgende Funktion deckt beide Möglichkeiten ab:

void insertArray(int* array, int length, int value, int pos) {
    int i;

    if (pos < length)
    {
        for (i = length; i > pos; i--)
            array[i] = array[i - 1];
        array[i] = value;
        length++;
    }
    else if (pos == length)
    {
        array[pos] = value;
        length++;
    }
}

Wenn die Einfügeposition index kleiner ist als die Anzahl der Zahlen length im Feld array[] dann schiebe alle Elemente ab Einfügeposition nach recht und füge das neue Element mit value ein. Sonst hänge das Element an das Ende des Feldes.

Löschen Bearbeiten

Möchte man ein Element aus dem Feld löschen, so genügt es, alle Elemente rechts um eine Position nach links zu verschieben und die Anzahl der vorhandenen Elemente im Feld um eines zu vermindern. Etwa so:

0 1 2 3 4 5 6 7 8 9
    ^ Element i=2 löschen
0 1 3 4 5 6 7 8 9

Hier ist der Algorithmus:

void deleteArray(int* array, int length, int index) {
    int i;

    if (index < length) {
        for (i = index + 1; i < length; i++)
            array[i - 1] = array[i];
        length--;
    }
}

Suchen Bearbeiten

Folgender Algorithmus der linearen Suche ist für ein kleines Feld anwendbar. Ab einer gewissen Anzahl von Elementen im Feld ist es ratsam, einen der schnelleren Suchalgorithmen zu verwenden.

int searchArray(int key, int* array, int length) {
    int i;

    for (i = 0; i < length; i++)
        if (array[i] == key)
            return i;
 
    return -1;
}

Jedes Element im Feld array[] wird mit key verglichen. Wenn der Vergleich stimmt, wird der Index des Elementes zurück gegeben. Wird kein Element gefunden, wird -1 zurück gegeben.

Sortieren Bearbeiten

Zum Sortieren von Zahlen in Feldern gibt es einen ganzen Haufen an Algorithmen. Detailierte Erklärungen mit Beispielen finden sich im dazugehörigen Kapitel Sortieralgorithmen.

Mehrdimensionale Felder Bearbeiten

Theoretisch lassen sich Felder mit unendlich vielen Dimensionen erstellen. Jedoch verweigert der Compiler bereits sehr früh die Kompilierung. Der Speicherbedarf steigert sich exponentiell. Von einem Feld mit mehr als vier Dimensionen ist bereits dringend abzuraten.

Zweidimensionales Feld:

#include <stdio.h>

int main(void) {
    // Deklaration eines zweidimensionalen Feldes
    int array[10][10];
    int i, j;

    // Füllen des Feldes mit Zahlen
    for (i = 0; i < 10; i++)
        for (j = 0; j < 10; j++)
            array[i][j] = i + j;
    
    // Ausgabe der Zahlen        
    for (i = 0; i < 10; i++)
        for (j = 0; j < 10; j++)
            printf("%d ", array[i][j]);
}

Vierdimensionales Feld:

#include <stdio.h>

int main(void) {
    // Deklaration eines vierdimensionalen Feldes
    int array[10][10][10][10];
    int i, j, k, l;

    // Füllen des Feldes mit Zahlen
    for (i = 0; i < 10; i++)
        for (j = 0; j < 10; j++)
            for (k = 0; k < 10; k++)
                for (l = 0; l < 10; l++)
                    array[i][j][k][l] = i + j + k + l;
    
    // Ausgabe der Zahlen        
    for (i = 0; i < 10; i++)
        for (j = 0; j < 10; j++)
            for (k = 0; k < 10; k++)
                for (l = 0; l < 10; l++)
                    printf("%d ", array[i][j][k][l]);

    return 0;
}

Die Algorithmen für den Umgang mit mehrdimensionalen Feldern funktionieren genau wie die für eindimensionale Felder. Man muss sie lediglich um die entsprechenden Dimensionen erweitern.

Beispiele Bearbeiten

Skalarprodukt zweier Vektoren Bearbeiten

In diesem Beispiel werden zwei Vektoren in einem eindimensionalen Feld gespeichert. Anschließend wird das Skalarprodukt berechnet und ausgegeben.

#include <stdio.h>
#define SIZE 3

int main(void) {
    // Vektoren definieren
    int vector_a[SIZE] = {1, 4, 5};
    int vector_b[SIZE] = {3, 2, 2};
    int i, result = 0;
    
    // Skalarprodukt berechnen
    for (i = 0; i < SIZE; i++)
        result += vector_a[i] * vector_b[i];
    
    printf("%d", result);
    
    return 0;
}

Überschreiten der Feldgröße Bearbeiten

Ein weiteres Beispiel für den Umgang mit Feldern in C ist das absichtliche Überschreiten der Feldgröße um andere Daten im Speicher zu manipulieren.
Es werden zwei Variablen a und c sowie ein Feld b mit der Größe 1 definiert. Die Variablen haben den Wert 1. Nun wird auf über den Index des Feldes b auf den Speicher links und rechts neben dem Feld zugegriffen. Dort befinden sich auch die Variablen a und c. Durch die folgende Zuweisung bekommen die beiden Variablen jetzt ebenfalls den Wert 2.

#include <stdio.h>

int main(void) {
    int a = 1;
    int b[1];
    int c = 1;
	
    // in Speicher schreiben
    b[-2] = 2;
    b[-1] = 2;
    b[0] = 2;
    b[1] = 2;
    b[2] = 2;
	
    printf("Wert von 'a': %d\n", a);
    printf("Wert von 'c': %d\n", c);
		
    return 0;
}

Als Ausgabe kann kommen:

Wert von 'a': 2
Wert von 'c': 2

Sobald der Compiler jedoch anfängt mit optimieren, landen a und c in CPU Registern und man überschreibt seinen eigenen Stack und somit die Rücksprungadresse. Der große Crash ist programmiert. Daher sind solche Tricksereien nicht zu empfehlen.