Zum Inhalt springen

C-Programmierung: Arrays

Aus Wikibooks

Eindimensionale Arrays

[Bearbeiten]

Nehmen Sie einmal rein fiktiv an, Sie wollten ein Programm für Ihre kleine Firma schreiben, das die Summe sowie den höchsten und den niedrigsten aller Umsätze einer Woche ermittelt. Es wäre natürlich sehr ungeschickt, wenn Sie die Variable umsatz1 bis umsatz7 deklarieren müssten. Noch umständlicher wäre die Addition der Werte und das Ermitteln des höchsten bzw. niedrigsten Umsatzes.

Für die Lösung des Problems werden stattdessen Arrays (auch als Felder oder Vektoren bezeichnet) benutzt. Arrays unterscheiden sich von normalen Variablen lediglich darin, dass sie einen Index besitzen. Statt umsatz1 bis umsatz7 zu deklarieren, reicht eine einmalige Deklaration aus:

float umsatz[7];
Visuelle Darstellung:
Index: | [0] | [1] | [2] | [3] | [4] | [5] | [6] | ...
Werte: | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | ...

Damit deklarieren Sie in einem Rutsch die Variablen umsatz[0] bis umsatz[6]. Beachten Sie unbedingt, dass auf ein Array immer mit dem Index 0 beginnend zugegriffen wird! Beispielsweise wird der fünfte Wert mit dem Index 4 (umsatz[4]) angesprochen! Dies wird nicht nur von Anfängern gerne vergessen und führt auch bei erfahreneren Programmierern häufig zu „Um-eins-daneben-Fehlern“.

Die Addition der Werte erfolgt in einer Schleife. Der Index muss dafür in jedem Durchlauf erhöht werden. In dieser Schleife testen wir gleichzeitig jeweils beim Durchlauf, ob wir einen niedrigeren oder einen höheren Umsatz als den bisherigen Umsatz haben:

#include <stdio.h>

int main( void )
{
    float umsatz[7];
    float summe, hoechsterWert, niedrigsterWert;
    int i;
	
    for( i = 0; i < 7; i++ )
    {
        printf( "Bitte die Umsaetze der letzten Woche eingeben: \n" );
        scanf( "%f", &umsatz[i] );
    }
	
    summe = 0;
    hoechsterWert = umsatz[0];
    niedrigsterWert = umsatz[0];
	
    for( i = 0; i < 7; i++ )
    {
        summe += umsatz[ i ];
        if( hoechsterWert < umsatz[i] )
            hoechsterWert = umsatz[i];
        //
        if( niedrigsterWert > umsatz[i] )
            niedrigsterWert = umsatz[i];
    }
	
    printf( "Gesamter Wochengewinn: %f \n", summe );
    printf( "Hoechster Umsatz: %f \n", hoechsterWert );
    printf( "Niedrigster Umsatz: %f \n", niedrigsterWert );
    return 0;
}

ACHTUNG: Bei einer Zuweisung von Arrays wird nicht geprüft, ob eine Feldüberschreitung vorliegt. So führt beispielsweise

  umsatz[10] = 5.0;

nicht zu einer Fehlermeldung, obwohl das Array nur 7 Elemente besitzt. Der Compiler gibt weder eine Fehlermeldung noch eine Warnung aus! Der Programmierer ist selbst dafür verantwortlich, dass die Grenzen des Arrays nicht überschritten werden. Ein Zugriff auf ein nicht vorhandenes Arrayelement kann zum Absturz des Programms oder anderen unvorhergesehenen Ereignissen führen! Des Weiteren kann dies ein sehr hohes Sicherheitsrisiko darstellen. Denn ein Angreifer kann dann über das Array eigene Befehle in den Arbeitsspeicher schreiben und vom Programm ausführen lassen. (Siehe  Bufferoverflow)

Mehrdimensionale Arrays

[Bearbeiten]

Ein Array kann auch aus mehreren Dimensionen bestehen. Das heißt, es wird wie eine Matrix dargestellt. Im Folgenden wird beispielsweise ein Array mit zwei Dimensionen definiert:

Abb 1. zweidimensionales Array im Speicher
int vararray[6][5]

Visuelle Darstellung:
   ___________________________________________   _____________
  /        /        /        /        /      /  /  /         /|
 /--------/--------/--------/--------/-------\  \-/-------- / |
| [0][0] | [0][1] | [0][2] | [0][3] | [0][4] /  / |        | /|
|--------|--------|--------|--------|--------\  \-|--------|/ |
| [1][0] | [1][1] | [1][2] | [1][3] | [1][4] /  / |        | /|
|--------|--------|--------|--------|--------\  \-|--------|/ |
| [2][0] | [2][1] | [2][2] | [2][3] | [2][4] /  / |        | /| 
|--------|--------|--------|--------|--------\  \-|--------|/ |
| [3][0] | [3][1] | [3][2] | [3][3] | [3][4] /  / |        | /|
|--------|--------|--------|--------|--------\  \-|--------|/ | 
| [4][0] | [4][1] | [4][2] | [4][3] | [4][4] /  / |        | /|
|--------|--------|--------|--------|--------\  \-|--------|/ |
| [5][0] | [5][1] | [5][2] | [5][3] | [5][4] /  / |        | /|
|--------|--------|--------|--------|--------\  \-|--------|/ |
|   __   |__    __|   __   |__    __|   __   /  / | __    _| /
\__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/ |//|
    __    __    __    __    __    __    __    __    __    _ / |
|__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/  \__/ | /| 
|--------|--------|--------|--------|-------------|--------|/ |
|        |        |        |        |                      | /|
|--------|--------|--------|--------|-------------|--------|/ |
|        |        |        |        |                      | /
|--------|--------|--------|--------|-------------|--------|/ 

Wie aus der Abbildung 1 ersichtlich, entspricht das mehrdimensionale Array im Speicher im Prinzip einem eindimensionalen Array. Dies muss nicht verwundern, da der Speicher ja selbst eindimensional aufgebaut ist.

Ein mehrdimensionales Array wird aber dennoch häufig verwendet, etwa wenn es darum geht, eine Tabelle, Matrix oder Raumkoordinaten zu speichern.


Mehrdimensionales Array genauer betrachtet

int Ary[2][3][3][5];                               4D
                                           ----------------->
 _________________________________________________    ________________________________________________
|4D-erste                                         |  |4D-zweite                                       | 
|                                                 |  |                                                | 
|    ____________________________________________ |  |  ____________________________________________  |
| 3D|3D-erste              1D                    ||  | |3D-erste             1D                     | |
|  ||                  --------->                ||  | |                  --------->                | |
|  ||    ______________________________________  ||  | |    ______________________________________  | |
|  || 2D|      ||      ||      ||      ||      | ||  | | 2D|      ||      ||      ||      ||      | | |
|  ||  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |
|  ||  ||______||______||______||______||______| ||  | |  ||______||______||______||______||______| | |
|  ||  | ______________________________________  ||  | |  | ______________________________________  | |
|  ||  V|      ||      ||      ||      ||      | ||  | |  V|      ||      ||      ||      ||      | | |
|  ||   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |
|  ||   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | |
|  ||    ______________________________________  ||  | |    ______________________________________  | |
|  ||   |      ||      ||      ||      ||      | ||  | |   |      ||      ||      ||      ||      | | |
|  ||   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |
|  ||   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | |
|  ||____________________________________________||  | |____________________________________________| |
|  | ____________________________________________ |  |  ____________________________________________  |
|  ||3D-zweite             1D                    ||  | |3D-zweite            1D                     | |
|  ||                  --------->                ||  | |                  --------->                | |
|  ||    ______________________________________  ||  | |    ______________________________________  | |
|  || 2D|      ||      ||      ||      ||      | ||  | | 2D|      ||      ||      ||      ||      | | |
|  ||  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |
|  ||  ||______||______||______||______||______| ||  | |  ||______||______||______||______||______| | |
|  ||  | ______________________________________  ||  | |  | ______________________________________  | |
|  V|  V|      ||      ||      ||      ||      | ||  | |  V|      ||      ||      ||      ||      | | |
|   |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | | 
|   |   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | |
|   |    ______________________________________  ||  | |    ______________________________________  | | 
|   |   |      ||      ||      ||      ||      | ||  | |   |      ||      ||      ||      ||      | | | 
|   |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | | 
|   |   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | | 
|   |____________________________________________||  | |____________________________________________| | 
|    ____________________________________________ |  |  ____________________________________________  | 
|   |3D-dritte            1D                     ||  | |3D-dritte             1D                    | | 
|   |                  --------->                ||  | |                  --------->                | | 
|   |    ______________________________________  ||  | |    ______________________________________  | | 
|   | 2D|      ||      ||      ||      ||      | ||  | | 2D|      ||      ||      ||      ||      | | | 
|   |  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |  ||2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |  
|   |  ||______||______||______||______||______| ||  | |  ||______||______||______||______||______| | | 
|   |  | ______________________________________  ||  | |  | ______________________________________  | | 
|   |  V|      ||      ||      ||      ||      | ||  | |  V|      ||      ||      ||      ||      | | | 
|   |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |   
|   |   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | | 
|   |    ______________________________________  ||  | |    ______________________________________  | | 
|   |   |      ||      ||      ||      ||      | ||  | |   |      ||      ||      ||      ||      | | | 
|   |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | ||  | |   |2D|1D ||2D|1D ||2D|1D ||2D|1D ||2D|1D | | |   
|   |   |______||______||______||______||______| ||  | |   |______||______||______||______||______| | | 
|   |____________________________________________||  | |____________________________________________| | 
|_________________________________________________|  |________________________________________________|   

Der erste Index des Arrays steht für die vierte Dimension, der zweite Index für die dritte Dimension, der dritte Index für die zweite Dimension und der letzte Index für die erste Dimension. Dies soll veranschaulichen, wie man sich ein mehrdimensionales Array vorstellen muss.


Veranschaulichung

Weil die Vorstellung von Objekten als mehrdimensionale Arrays abseits von 3 Dimensionen (Würfel) schwierig ist, sollte man sich Arrays lieber als doppelte Fortschrittsbalken (wie bei einem Brennprogramm oft üblich) oder als Maßeinheit (z. B. Längenangaben) vorstellen. Um es an einem dieser genannten Beispiele zu veranschaulichen:

Man stellt sich einen Millimeter als erstes Array-Element (Feld) vor.

1 Feld = 1 mm

int Array[10];
#10 mm = 1 cm
#Array[Eine-Dimension (10 Felder)] = 1 cm

Natürlich könnte man mehr Felder für die erste Dimension verwenden, doch sollte man es zu Gunsten der Übersichtlichkeit nicht übertreiben.

int Array[10][10];
#10 mm x 10 = 1 dm
#Array[Zwei Dimensionen (Zehn Zeilen (eine Zeile mit je 10 Feldern)] = 1 dm

Die Anzahl der weiteren Feldblöcke (oder der gesamten Felder) wird durch die angegebene Zeilenanzahl bestimmt.

int Array[10][10][10]
#10 mm x 10 x 10 = 1 m
#Array[Drei-Dimensionen (Zehn mal _2D-Blöcke_ (die mit je 10 Feld-Blöcken, die wiederum mit je 10 Feldern)) ] = 1 m

Insgesamt enthält dieses Array somit 1000 Felder, in denen man genau so viele Werte speichern könnte wie Felder vorhanden. Die Dimensionen verlaufen von der kleinsten (1D) außen rechts zur größten (hier 3D) nach außen links.

Ab der dritten Dimension folgt es immer dem gleichen Muster.


Hier noch ein Beispielprogramm zum Verständnis:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define WARY1  10
#define WARY2  10
#define WARY3  10 


int main( void )
{
    srand( time( 0 ) );
    
    int a, b, c;
    int ZAry[WARY1][WARY2][WARY3];
    //
    for( a = 0; a < WARY1; ++a )
    {
        for( b = 0; b < WARY2; ++b )
        {
            for( c = 0; c < WARY3; ++c )
            {
                ZAry[a][b][c] = rand( );
            }
        }
    }
    //
    for( a = 0; a < WARY1; ++a )
    {
        for( b = 0; b < WARY2; ++b )
        {
            for( c = 0; c < WARY3; ++c )
            {
                printf( "Inhalt von Z-Ary[%d][%d][%d] ", a, b, c );
                printf( "ist: %d \n", ZAry[a][b][c] );
            }
            printf( "Weiter mit Eingabetaste && Verlassen mit STRG-C. \n" );
            getchar( );
        }
    }
    //
    return 0;
}

Arrays initialisieren

[Bearbeiten]

Es gibt zwei Schreibstile für die Initialisierung eines Arrays. Entweder die Werte gruppiert untereinander schreiben:

int Ary[2][4] = {
                    {1, 2, 3, 4},
                    {5, 6, 7, 8},
                };

oder alles hintereinander schreiben:

int Ary[2][4] = { 1, 2, 3, 4, 5, 6, 7, 8 };


Grundsätzlich ist es ratsam, ein Array immer zu initialisieren, damit man beim späteren Ausführen des Programms nicht durch unerwartete Ergebnisse überrascht wird. Denn ohne eine Initialisierung weiß man nie, welchen Wert die einzelnen Array-Elemente beinhalten.


Beispiel für eine Initialisierung mit 0:

int a[5] = { 0 };  /* alle 5 Array-Elemente besitzen den Wert 0 */

Für char-Arrays gibt es eine zusätzliche Initialisierungsmöglichkeit mit einem Stringliteral:

char a[5] = "";  /* alle 5 Array-Elemente besitzen den Wert 0 */

Werden bei der Initialisierung eines Arrays weniger Werte als vorhandene Elemente angegeben, werden alle endenden Elemente automatisch mit 0 vorbelegt.

int  a[5] = { 1, 2 };  /* a[0]=1, a[1]=2, a[2]=0, a[3]=0, a[4]=0 */
char a[5] = "ab";      /* a[0]='a', a[1]='b', a[2]='\0', a[3]='\0', a[4]='\0' */

Dieses Verfahren empfiehlt sich insbesondere bei großen Arrays, denn viele hundert Einzelwerte anzugeben ist sehr unübersichtlich und fehlerträchtig.

Sind Array global oder mittels Speicherklasse static definiert, sind automatisch alle Elemente mit 0 vorbelegt, auch ohne Angabe von Initialisierungswerten ( {0} ).
Es empfiehlt sich wegen der Lesbarkeit des Codes aber trotzdem, hier die 0-Werte anzugeben.
Identische Definitionen eines Arrays:

static int a[5];
static int a[5] = { 0 };
static int a[5] = { 0, 0 };
static int a[5] = { 0, 0, 0, 0, 0 };

Syntax der Initialisierung

[Bearbeiten]

Es gibt zwei Möglichkeiten, ein Array zu initialisieren: entweder eine teilweise oder eine vollständige Initialisierung. Bei einer Initialisierung steht ein Zuweisungsoperator nach dem deklarierten Array, gefolgt von einer in geschweiften Klammern stehenden Liste von Werten, die durch Kommata getrennt werden. Diese Liste wird der Reihenfolge nach ab dem Index 0 den Array-Elementen zugewiesen.

Eindimensionales Array vollständig initialisiert

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

Index  | Inhalt
----------------
Ary[0] = 10
Ary[1] = 20
Ary[2] = 30
Ary[3] = 40
Ary[4] = 50


Fehlende Größenangabe bei vollständig initialisierten eindimensionalen Arrays

Wenn die Größe eines vollständig initialisierten eindimensionalen Arrays nicht angegeben wurde, erzeugt der Compiler ein Array, das gerade groß genug ist, um die Werte aus der Initialisierung aufzunehmen. Deshalb ist:

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

das gleiche wie:

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

Ob man die Größe angibt oder weglässt ist jedem selbst überlassen, jedoch ist es zu empfehlen, sie anzugeben.


Eindimensionales Array teilweise initialisiert

[Bearbeiten]
int Ary[5] = { 10, 20, 30 };

Index  | Inhalt
----------------
Ary[0] = 10
Ary[1] = 20
Ary[2] = 30
Ary[3] = 0
Ary[4] = 0

Wie man in diesem Beispiel deutlich erkennt, werden nur die ersten drei Array-Elemente mit den Indizes 0, 1 und 2 initialisiert. Die beiden letzten Array-Elemente mit den Indizes 3 und 4 sind hingegen leer geblieben. Diese werden bei solchen nur teilweise initialisierten Arrays vom Compiler mit dem Wert 0 gefüllt, um den Speicherplatz zu reservieren.


Fehlende Größenangabe bei teilweise initialisierten eindimensionalen Arrays

Bei einem teilweise initialisierten eindimensionalen Array führt eine fehlende Größenangabe dazu, dass die Größe des Arrays womöglich nicht ausreichend ist, weil nur so viele Array-Elemente vom Compiler erstellt werden, um die Werte aus der Liste aufzunehmen. Deshalb sollte man immer die Größe angeben!

Mehrdimensionales Array vollständig initialisiert

[Bearbeiten]
int Ary[4][5] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		}


Visuelle Darstellung:
---------------------
Index	  | Inhalt
--------------------
Ary[0][0] = 10
Ary[0][1] = 11
Ary[0][2] = 12
Ary[0][3] = 13
Ary[0][4] = 14

Ary[1][0] = 24
Ary[1][1] = 25
Ary[1][2] = 26
Ary[1][3] = 27
Ary[1][4] = 28

Ary[2][0] = 30
Ary[2][1] = 31
Ary[2][2] = 32
Ary[2][3] = 33
Ary[2][4] = 34

Ary[3][0] = 44
Ary[3][1] = 45
Ary[3][2] = 46
Ary[3][3] = 47
Ary[3][4] = 48


Fehlende Größenangabe bei vollständig initialisierten mehrdimensionalen Arrays

Bei vollständig initalisierten mehrdimensionalen Array sieht es mit dem Weglassen der Größenangabe etwas anders aus als bei vollständig initialisierten eindimensionalen Arrays. Denn wenn ein Array mehr als eine Dimension besitzt, darf man nicht alle Größenangaben weg lassen. Grundsätzlich sollte man nie auf die Größenangaben verzichten. Notfalls ist es gestattet, die erste (und nur diese) weg zu lassen. Mit „erste“ ist immer die linke gemeint, die direkt an den Array Variablennamen angrenzt.


Wenn also eine Größenangabe (die erste) des Arrays nicht angegeben wurde, erzeugt der Compiler ein Array das gerade groß genug ist, um die Werte aus der Initialisierung aufzunehmen. Deshalb ist:

int Ary[4][5] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

das gleiche wie:

int Ary[ ][5] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

Ob man die Größe angibt oder weglässt ist jedem selbst überlassen, jedoch ist es zu empfehlen, sie anzugeben.


Falsch hingegen wären:

int Ary[5][ ] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

oder:

int Ary[ ][ ] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

genau wie:

int Ary[ ][4][ ] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

und:

int Ary[ ][ ][5] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32, 33, 34 },
		    { 44, 45, 46, 47, 48 },
		};

Mehrdimensionales Array teilweise initialisiert

[Bearbeiten]
int Ary[4][5] = { 
		    { 10, 11, 12, 13, 14 },
		    { 24, 25, 26, 27, 28 },
		    { 30, 31, 32 },
		}

Index	  | Inhalt
--------------------
Ary[0][0] = 10
Ary[0][1] = 11
Ary[0][2] = 12
Ary[0][3] = 13
Ary[0][4] = 14

Ary[1][0] = 24
Ary[1][1] = 25
Ary[1][2] = 26
Ary[1][3] = 27
Ary[1][4] = 28

Ary[2][0] = 30
Ary[2][1] = 31
Ary[2][2] = 32
Ary[2][3] = 0
Ary[2][4] = 0

Ary[3][0] = 0
Ary[3][1] = 0
Ary[3][2] = 0
Ary[3][3] = 0
Ary[3][4] = 0

Das teilweise Initalisieren eines mehrdimensionalen Arrays folgt genau dem selben Muster wie auch schon beim teilweise initalisierten eindimensionalen Array. Hier werden auch nur die ersten 13 Felder mit dem Index [0|0] bis [2|2] gefüllt. Die restlichen werden vom Compiler mit 0 gefüllt.

Es ist wichtig nicht zu vergessen, dass die Werte aus der Liste den Array-Elementen ab dem Index Nummer Null übergeben werden und nicht erst ab dem Index Nummer Eins! Außerdem kann man auch keine Felder überspringen, um den ersten Wert aus der Liste beispielsweise erst dem fünften oder siebten Array-Element zu übergeben!


Fehlende Größenangabe bei teilweise initialisierten Mehrdimensionalen Arrays

Die Verwendung von teilweise initialisierte mehrdimensionale Arrays mit fehlender Größenangabe macht genauso wenig Sinn wie auch bei teilweise initialiserten Eindimensionalen Array. Denn eine fehlende Größenangabe führt in solch einem Fall dazu, dass die Größe des Arrays womöglich nicht ausreichend ist, weil nur genug Array-Elemente vom Compiler erstellt wurden um die Werte aus der Liste auf zu nehmen. Deshalb sollte man bei solchen niemals vergessen die Größe mit anzugeben.

Arrays und deren Speicherplatz

[Bearbeiten]

Die Elementgröße eines Arrays hängt zum einen vom verwendeten Betriebssystem und zum anderen vom angegebenen Datentyp ab, mit dem das Array deklariert wurde.

#include <stdio.h>
#include <stdlib.h>

signed char siAry1[200];
signed short siAry2[200];
signed int siAry3[200];
signed long int siAry4[200];
signed long long int siAry5[200];

unsigned char unAry1[200];
unsigned short unAry2[200];
unsigned int unAry3[200];
unsigned long int unAry4[200];
unsigned long long int unAry5[200];

float Ary6[200];
double Ary7[200];
long double Ary8[200];


int main( void )
{
    printf( "Datentyp des Elements | Byte (Elementgröße) \n" );
    printf( "Signed: \n" );
    printf( "signed char        =    %d Byte \n", sizeof(signed char) );	
    printf( "signed short        =    %d Byte \n", sizeof(signed short) );
    printf( "signed int        =    %d Byte \n", sizeof(signed int) );
    printf( "signed long int        =    %d Byte \n", sizeof(signed long int) );
    printf( "signed long long int    =    %d Byte \n\n", sizeof(signed long long int) );
    //
    printf( "Unsigned: \n" );
    printf( "unsigned char        =    %d Byte \n", sizeof(unsigned char) );
    printf( "unsigned short        =    %d Byte \n", sizeof(unsigned short) );
    printf( "unsigned int        =    %d Byte \n", sizeof(unsigned int) );
    printf( "unsigned long int    =    %d Byte \n", sizeof(unsigned long int) );
    printf( "unsigned long long int    =    %d Byte \n\n", sizeof(unsigned long long int) );
    //
    printf( "Signed ohne prefix \n" );
    printf( "float            =    %d Byte \n", sizeof(float) );
    printf( "double            =    %d Byte \n", sizeof(double) );
    printf( "long double        =    %d Byte \n\n\n", sizeof(long double) );
    

    printf( "Groeße, mit verschiedenen Datentyp, eines arrays mit 200Feldern \n" );
    printf( "Signed: \n" );
    printf( "Groeße von siAry als signed char = %d Byte \n", sizeof(siAry1) );
    printf( "Groeße von siAry als signed short = %d Byte \n", sizeof(siAry2) );
    printf( "Groeße von siAry als signed int = %d Byte \n", sizeof(siAry3) );
    printf( "Groeße von siAry als signed long int = %d Byte \n", sizeof(siAry4) );
    printf( "Groeße von siAry als signed long long int = %d Byte \n\n", sizeof(siAry5) );
    //
    printf( "Unsigned: \n" );
    printf( "Groeße von unAry als unsigned char = %d Byte \n", sizeof(unAry1) );
    printf( "Groeße von unAry als unsigned short = %d Byte \n", sizeof(unAry2) );
    printf( "Groeße von unAry als unsigned int = %d Byte \n", sizeof(unAry3) );
    printf( "Groeße von unAry als unsigned long int = %d Byte \n", sizeof(unAry4) );
    printf( "Groeße von unAry als unsigned long long int = %d Byte \n\n", sizeof(unAry5) );
    //
    printf( "Signed ohne prefix \n" );
    printf( "Groeße von Ary als float = %d Byte \n", sizeof(Ary6) );
    printf( "Groeße von Ary als double = %d Byte \n", sizeof(Ary7) );
    printf( "Groeße von Ary als long double = %d Byte \n\n", sizeof(Ary8) );

    return 0;
}

Die Speicherplatzgröße eines gesamten Arrays hängt vom verwendeten Datentyp bei der deklaration und von der Anzahl der Elemente die es beinhaltet ab.


Die maximale Größe eines Array wird nur durch den verfügbaren Speicher limitiert.

Den Array-Speicherpaltz ermitteln:

Array Größe 	 	 =	[ (Anzahl der Elemente) x (Datentyp) ]
----------------------------------------------------------------------------
char Ary[500]            |    [ 500(Elemente) x 1(Typ.Größe) ] =  500 Byte
short Ary[500]           |    [ 500(Elemente) x 2(Typ.Größe) ] = 1000 Byte
int Ary[500]             |    [ 500(Elemente) x 4(Typ.Größe) ] = 2000 Byte
long int Ary[500]        |    [ 500(Elemente) x 4(Typ.Größe) ] = 2000 Byte
long long int Ary[500]   |    [ 500(Elemente) x 8(Typ.Größe) ] = 4000 Byte
 
float Ary[500]           |    [ 500(Elemente) x 4(Typ.Größe) ] = 2000 Byte
double Ary[500]          |    [ 500(Elemente) x 8(Typ.Größe) ] = 4000 Byte
long double Ary[500]     |    [ 500(Elemente) x 12(Typ.Größe)] = 6000 Byte
____________________________________________________________________________

Anmerkung: Bei einem 64bit System unterscheiden sich die Werte.

Übergabe eines Arrays an eine Funktion

[Bearbeiten]

Bei der Übergabe von Arrays an Funktionen wird nicht wie bei Variablen eine Kopie übergeben, sondern immer ein Zeiger auf das erste Element des Arrays.

Das folgende Beispielprogramm zeigt die Übergabe eines Arrays an eine Funktion:

#include <stdio.h>

void function( int feld[ ] )
{
    feld[1] = 10;
    feld[3] = 444555666;
    feld[8] = 25;
}

int main( void )
{
    int feld[9] = { 1, 2, 3, 4, 5, 6 };
    printf( "Der Inhalt des fuenften array Feldes ist: %d \n", feld[4] );
    printf( "Der Inhalt des sechsten array Feldes ist: %d \n\n", feld[5] );
	
    function( feld );
    printf( "Der Inhalt des ersten array Feldes ist: %d \n", feld[0]);
    printf( "Der Inhalt des zweiten array Feldes ist: %d \n", feld[1] );
    printf( "Der Inhalt des dritten array Feldes ist: %d \n", feld[2]);
    printf( "Der Inhalt des vierte array Feldes ist: %d \n", feld[3]);
    printf( "Der Inhalt des fuenften array Feldes ist: %d \n", feld[4] );
    printf( "Der Inhalt des neunten array Feldes ist: %d \n\n", feld[8] );
    
    return 0;
}

Nach dem Ausführen erhalten Sie als Ausgabe:

Der Inhalt des fuenften array Feldes ist: 5 
Der Inhalt des sechsten array Feldes ist: 6 

Der Inhalt des ersten array Feldes ist: 1 
Der Inhalt des zweiten array Feldes ist: 10 
Der Inhalt des dritten array Feldes ist: 3 
Der Inhalt des vierte array Feldes ist: 444555666 
Der Inhalt des fuenften array Feldes ist: 5 
Der Inhalt des neunten array Feldes ist: 25 

Mit dem Funktionsaufruf function( feld ) wird ein Zeiger auf das erste Element des Arrays an das Unterprogramm übergeben. Ausgehend von der Adresse des ersten Elements können die Adressen der nächsten Elemente berechnet werden und somit auf die Werte der Elemente zugegriffen werden.

Hier zwei gleichbedeutende Schreibweisen:

void function( int feld[] )
void function( int *feld )

Die alternative Darstellungsform ließe sich also wie folgt realisieren:

#include <stdio.h>

void function( int *feld )
{
    feld[1] = 10;
    feld[3] = 444555666;
    feld[8] = 25;
}

int main( void )
{
    int feld[9] = { 1, 2, 3, 4, 5, 6 };
    printf( "Der Inhalt des fuenften array Feldes ist: %d \n", feld[4] );
    printf( "Der Inhalt des sechsten array Feldes ist: %d \n\n", feld[5] );
	
    function( feld );
    printf( "Der Inhalt des ersten array Feldes ist: %d \n", feld[0]);
    printf( "Der Inhalt des zweiten array Feldes ist: %d \n", feld[1] );
    printf( "Der Inhalt des dritten array Feldes ist: %d \n", feld[2]);
    printf( "Der Inhalt des vierte array Feldes ist: %d \n", feld[3]);
    printf( "Der Inhalt des fuenften array Feldes ist: %d \n", feld[4] );
    printf( "Der Inhalt des neunten array Feldes ist: %d \n\n", feld[8] );
    
    return 0;
}

Mehrdimensionale Arrays übergeben Sie entsprechend der Dimensionszahl wie eindimensionale. [] und * lassen sich auch hier in geradezu abstrusen Möglichkeiten vermischen, doch dabei entsteht unleserlicher Programmcode. Hier eine korrekte Möglichkeit, ein zweidimensionales Feld an eine Funktion zu übergeben:

#include <stdio.h>

void function( int feld[2][5] )
{
    feld[1][2] = 55;
}

int main( void )
{
    int feld[2][5] = { 
                        { 10, 11, 12, 13, 14 },
                        { 20, 21, 22, 23, 24 } 
                     };

    printf( "%d \n", feld[1][2] );

    function( feld );
    printf( "%d \n", feld[1][2] );

    return 0;
}

Zeigerarithmetik

[Bearbeiten]

Auf Zeiger können Additions-, Subtraktions- sowie Vergleichsoperatoren angewendet werden. Die Verwendung anderer Operatoren, wie beispielsweise des Multiplikations- oder Divisionsoperators, ist dagegen nicht erlaubt.

Die Operatoren können verwendet werden, um innerhalb eines Arrays auf verschiedene Elemente zuzugreifen, oder die Position innerhalb des Arrays zu vergleichen. Hier ein kurzes Beispiel um es zu verdeutlichen:

#include <stdio.h>

int main( void )
{
    int *ptr;
    int a[5] = { 1, 2, 3, 5, 7 };
	

    ptr = &a[0];
    printf( "a) Die Variable enthält den Wert: %d \n", *ptr );
    //
    ptr += 2;
    printf( "b) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
    //
    ptr -= 1;
    printf( "c) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
    //
    ptr += 3;
    printf( "d) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
    //
    ptr -= 1;
    printf( "e) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
    return 0;
}

Wir deklarieren einen Zeiger sowie ein Array und weisen dem Zeiger die Adresse des ersten Elementes zu (Abb. 2). Da der Zeiger der auf das erste Element im Array gerichtet ist äquivalent zum Namen des Array ist, kann man diesen auch kürzen. Deshalb ist:

Abb. 2
ptr = &a[0]; 

das gleiche wie:

ptr = a;


Auf den Zeiger ptr kann nun beispielsweise der Additionsoperator angewendet werden. Mit dem Ausdruck

Abb. 3
ptr += 2

wird allerdings nicht etwa a[0] erhöht, sondern ptr zeigt nun auf a[2] (Abb. 3).

Wenn ptr auf ein Element des Arrays zeigt, dann zeigt ptr += 1 auf das nächste Element, ptr += 2 auf das übernächste Element usw. Wendet man auf einen Zeiger den Dereferenzierungsoperator (*) an, so erhält man den Inhalt des Elements, auf das der Zeiger gerade zeigt. Wenn beispielsweise ptr auf a[2] zeigt, so entspricht *ptr dem Wert des dritten Elements des Arrays.

Auch Inkrement- und Dekrementoperator können auf Zeiger auf Vektoren angewendet werden. Wenn ptr auf a[2] zeigt, so erhält man über ptr++ die Adresse des Nachfolgeelements a[3]. Hier ein weiteres Beispiel um es zu veranschaulichen:

#include <stdio.h>

int main( void )
{
    int *ptr;
    int a[5] = { 1, 2, 3, 5, 7 };
	
    ptr = &a[0];
    printf( "a) Die Variable enthält den Wert: %d \n", *ptr );
        //
        ptr += 2;
    printf( "b) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
        //
        ptr -= 1;
    printf( "c) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
        //
        ptr += 3;
    printf( "d) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
        //
        ptr -= 1;
    printf( "e) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
    

    ptr--;
    printf( "a) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
    //    
    --ptr;
    printf( "b) Nach der Subtraktion enthält die Variable den Wert: %d \n", *ptr );
    //
    ptr++;
    printf( "c) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
    //
    ++ptr; 
    printf( "d) Nach der Addition enthält die Variable den Wert: %d \n", *ptr );
    return 0;
}


Um die neue Adresse berechnen zu können, muss der Compiler die Größe des Zeigertyps kennen. Deshalb ist es nicht möglich, die Zeigerarithmetik auf den Typ void* anzuwenden.

Grundsätzlich ist zu anzumerken, dass sich der []-Operator in C aus den Zeigeroperationen heraus definiert. Daraus ergeben sich recht kuriose Möglichkeiten: So ist a[b] als *(a+b) definiert, was wiederum gleichbedeutend mit *(b+a) und somit b[a] ist. So kommt es, dass 4[a] das gleiche Ergebnis liefert, wie a[4] – nämlich das 5. Element vom Array a. Das Beispiel sollte man allerdings nur zur Verdeutlichung der Bedeutung des []-Operators verwenden und nicht wirklich anwenden.

Zeigerarithmetik auf Char-Arrays

[Bearbeiten]

Die Zeigerarithmetik bietet natürlich auch eine Möglichkeit, char-Arrays zu verarbeiten. Ein Beispiel aus der Kryptografie verdeutlicht das Prinzip:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char satz[1024];
    char *p_satz; 
    int satzlaenge;
    char neuersatz[1024];
    char *p_neuersatz;

    fgets( satz, sizeof(satz), stdin );
    p_neuersatz = neuersatz;

    for( p_satz = satz; p_satz < satz + ( strlen(satz)-1 ); p_satz += 2 )
    {
        *p_neuersatz = *p_satz;
        ++p_neuersatz;
    }

    for( p_satz = satz+1; p_satz < satz + ( strlen(satz)-1 ); p_satz += 2 )
    {
        *p_neuersatz = *p_satz;
        ++p_neuersatz;
    }

    *p_neuersatz = '\0';

    printf( "Original Satz: %s \n", satz );
    printf( "Verschluesselter Satz: %s \n", neuersatz );
    printf( "Der String ist %d Zeichen lang \n", strlen(satz)-1 );
    return 0; 
}

Sehen wir uns dieses Beispiel etwas genauer an:

Als erstes wird der zusätzliche Header string.h eingebunden, um die Funktion strlen() zum Messen der Länge von Zeichenketten nutzen zu können. Vorsicht: strlen() ist – im Unterschied zu sizeof() – eben kein Operator, sondern eine Funktion.

In Zeile 12 wird mit der Funktion fgets() über die Standardeingabe (stdin) eine Zeichenkette entgegengenommen und diese im Array satz[] abgelegt. Die Länge der Zeichenkette entspricht aufgrund von sizeof() der Größe des Arrays. Anschließend wird der Zeiger *p_neuersatz noch auf das erste Element des Arrays neuersatz[] gesetzt. Zur Erinnerung:

p_neuersatz = neuersatz;

entspricht

p_neuersatz = &neuersatz[0];

In den folgenden Schleife wird der Zeiger *p_satz zuerst auf das erste bzw. zweite Element des Arrays satz[] gerichtet und dann pro Schleifendurchlauf um zwei erhöht, so lange der Wert von p_satz kleiner ist als die Länge der Zeichenkette. Zugleich wird pro Durchlauf das Array-Element, auf das der p_satz-Zeiger gerichtet ist, an die Zeigervariable p_neuersatz (und damit ans jeweilige neuersatz[]-Element) übergeben und diese anschließend erhöht, um im folgenden Durchlauf auf das nächste Array-Element zugreifen zu können.

Mit der ersten Schleife werden also zuerst alle geraden Elemente (also satz[0], satz[2], etc.) in das Char-Array neuersatz[] geschrieben, mit der zweiten Schleife dann alle ungeraden Elemente (also satz[1], satz[3], etc.). Hello World! würde also beispielsweise zu HloWrdel ol!.

Strings

[Bearbeiten]

C besitzt im Gegensatz zu vielen anderen Sprachen keinen Datentyp für Strings (Zeichenketten). Stattdessen werden für Zeichenketten char-Arrays verwendet. Das Ende des Strings ist immer durch das sogenannte String-Terminierungszeichen \0 gekennzeichnet. Beispielsweise wird mit

char text[5]="Wort";

beziehungsweise

char text[]="Wort";

ein String definiert. Das char-Array hat fünf (nicht vier!) Elemente – das fünfte Element ist \0. Ausführlich geschrieben entsprechen diese Definitionen

char text[5] = {'W','o','r','t','\0'};

beziehungsweise

char text[]  = {'W','o','r','t','\0'};

oder auch

char text[5];
text[0]='W';
text[1]='o';
text[2]='r';
text[3]='t';
text[4]='\0';

Zu beachten ist dabei, dass einzelne Zeichen mit Hochkommata (') eingeschlossen werden müssen. Strings dagegen werden immer mit Anführungszeichen (") markiert. Im Gegensatz zu 'W' in Hochkommata entspricht "W" dem Zeichen 'W' und zusätzlich dem Terminierungszeichen '\0'.