Damit in C mit Variablen gearbeitet werden kann, muss für diese zunächst Speicherplatz reserviert werden. Dies geschieht, indem dem Variablennamen ein Datentyp zuordnet wird. Neben Variablen besitzen auch Funktionen in C einen Datentyp, der dem Typ des durch die Funktion bereitgestellten Funktionswertes entspricht.
In C wird zwischen Basisdatentypen, deren Modifikationen (durch Voranstellen der Typ-Modifizierer short, long, double, signed und unsigned) und erweiterten Datentypen unterschieden.
Als Basisdatentypen stehen zur Verfügung:
Der Typ int erlaubt die Anwendung aller Modifizierer. Der Basisdatentyp
char lässt sich nur mit den Modifizierern signed und
unsigned kombinieren. Auf den Datentyp float lässt sich
der Modifizierer long anwenden. Dabei entsteht der Datentyp double
(long float), mit dem long nochmals kombiniert
werden kann (long double).
Die Tabelle 8.3 gibt eine Übersicht über die Datentypen, ihren
Modifikationen und ihrer Größe im Speicher. Die Größe
einiger Datentypen ist dabei abhängig vom verwendeten Computer und
kann leicht mit dem sizeof-Operator ermittelt werden.
Einen Datentyp Boolean, mit dem Wertbelegungen von wahr und falsch möglich
wären, gibt es in C nicht. Bei logischen Operationen wird deshalb
jeder Wert ungleich Null als wahr und jeder Wert gleich Null als falsch
interpretiert.
Datentyp char
Die char-Vereinbarung wird verwendet um Zeichen abzuspeichern.
Der Integer-Wert, der in der Variablen abgespeichert wird, ist dem ASCII-Code
zugeordnet. Jede Variable vom Typ char, die in einem arithmetischen
Ausdruck steht, wird für die Berechnung automatisch in eine int-Zahl
umgewandelt.
Beispiel:
char a;
a = 's';
printf("Der Buchstabe a lautet: %c\n",a);
printf("Der Zahlenwert von a lautet:
%d\n",a);
In der ersten Zeile des Beispiels wird die Variable a
als char deklariert. In der zweiten Zeile erfolgt die Initialisierung
von a, indem ihm ein Zeichen zugewiesen wird. Das Zeichen
muss dafür in einfachen Hochkommas stehen. Mit den beiden printf-Anweisungen
wird der Inhalt von a ausgegeben. Die erste Anweisung gibt ein 's'
, die zweite gibt die Zahl 115 auf dem Bildschirm aus. Der Grund hierfür
liegt darin, das ein Zeichen mit seinem ASCII-Code abgespeichert wird.
So kann ohne große Umwandlung der ASCII-Code eines Zeichens abgefragt
werden.
Datentyp int
Zum int-Datentyp zählen : int, unsigned int, long int, unsigned long int, short int und unsigned short int.
Da diese Vereinbarungen für die Maschine am einfachsten zu verarbeiten
sind, ergeben sich dadurch sehr kurze Ausführungszeiten bei Rechen-
und Zählanweisungen.
Beispiel:
int a, b;
unsigned c;
float d;
long x = 200000000, y;
a = -4; b = 6; d = 12.75;
c = a ;
b = x;
Die Variable d wird als 4 Byte große float-Zahl,
mit 6 stelliger Genauigkeit definiert. Die als unsigned definierte
Variable wird in einen int Typ umgewandelt und kann somit auch negative
Werte annehmen. Die Zuweisung b = x; wird vom Compiler nicht als
Fehler erkannt, liefert aber ein falsches Ergebnis. Deshalb sollten Zuweisungen
zwischen unterschiedlichen Typen grundsätzlich vermieden werden.
Datentypen float und double
Die Datentypen float und double dienen zur Darstellung
von Fließkommazahlen. Dabei steht float für einfache,
double für hohe und long double für sehr hohe Genauigkeit.
Die Genauigkeit bestimmt, wieviele Dezimalziffern einer Zahl gespeichert
werden können. Das heißt, dass bei einer Genauigkeit von 6 Stellen,
die beiden Zahlen 12345.6 und 12345.67 nicht unterschieden werden. Das
gleiche gilt für die beiden Zahlen 0.000123456 und 0.0001234567.
Beispiel:
float f;
double d;
long double ld= 1.3E1234;
Das Beispiel zeigt die Deklaration einer float-, double- und long-double-Zahl, wobei letztere mit einem Wert initialisiert wird.
Bei Zuweisungen von einer Variablen zu einer anderen sollten die Typen
der beiden Variablen übereinstimmen. Stimmen die beiden Typen nicht
überein, so liefert der Compiler meist eine Fehlermeldung oder es
wird beim Programmlauf eine Typanpassung durchgeführt. Wird z.B. eine
Variable des Typs float oder double einer Variablen
vom Typ int zugewiesen, so wird sie in int umgewandelt.
Hierbei wird der Nachkommateil der Zahl einfach abgeschnitten.
Zusammengesetzte Datentypen
Zu den zusammengesetzten Datentypen gehören:
Datentyp Array
Unter einem Array versteht man Folgen von gleichartigen Variablen, die über einen gemeinsamen Namen und über den Arrayauswahloperator [ ] ansprechbar sind. Die einzelnen Elemente eines Arrays haben dabei immer den gleichen Typ.
Häufig verwendete Synonyme für Arrays sind "Vektor" (für eindimensionale Arrays), "Tabelle" und "Datenfeld".
Eindimensionale Arrays
Eindimensionale Arrays besitzen n Elemente in einer Ebene. Die einzelnen Elemente sind von 0 bis (n-1) durchnummeriert. Eine Arraydeklaration lässt sich wie folgt formulieren:
datentyp arrayname[elementanzahl]
Um ein Array zu initialisieren, gibt es drei Möglichkeiten:
Bei der Definition eines Arrays können bereits die entsprechenden
Werte in der richtigen Reihenfolge angegeben werden. Die Werte sind dabei
von geschweiften Klammern einzuschließen. Der Compiler überprüft,
ob die Anzahl der angegebenen Werte mit der Arraygröße übereinstimmt.
Sind mehr Werte angegeben, als Elemente vorhanden, gibt der Compiler eine
Fehlermeldung aus. Stimmt die Anzahl mit der Arraygröße überein,
werden den Elementen die Werte zugewiesen. Ist die Anzahl kleiner, werden
alle Elemente, für die kein Wert vorgegeben ist, mit Null initialisiert.
Fehlt die Angabe der Elementanzahl bei initialisierten Arrays, so wird
sie aus der Anzahl der vorgegebenen Werte berechnet.
Beispiel:
int feld[4]= { 15, -2, 7, -18 };
int feld2[]= { 0, -5, -10 };
int feld3[10];
In der ersten Zeile wird ein Feld mit einer Arraygröße von 4 Elementen definiert und initialisiert.
In der zweiten Zeile wird ein zweites Feld definiert und initialisiert. Da hier die Angabe über die Größe des Arrays fehlt, wird dies aus der Anzahl der vorgegebenen Werte berechnet.
In der dritten Zeile wird feld3 definiert, ohne jedoch Werte zuzuweisen.
Da es in C keinen Datentyp für Strings gibt, bietet es sich an,
dafür ein Array zu deklarieren.
Beispiel:
char name[5]= "anna";
name[0]= 'A';
Über den Zuweisungsoperator "=" ist es nur möglich, den Elementen
einzelne Zeichen zuzuweisen. Als Abschluss sollte immer ein Ende-Zeichen
(Nullbyte) stehen. Die einzelnen Zeichen stehen dabei in einfachen Hochkommas.
Soll einem Array im Programm eine ganze Zeichenkette zugewiesen werden,
so kann dafür die Funktion strcpy verwendet werden.
Mehrdimensionale Arrays
Mehrdimensionale Arrays sind eindimensionale Arrays, die als Elemente wieder ein Array enthalten. Die Darstellung mehrdimensionaler Arrays kann folgendermaßen dargestellt werden:
datentyp arrayname [e1] [e2] ... [en]
e1, e2, ... , en geben an, wieviele Elemente in den entsprechenden Dimensionen vorhanden sind.
Die Initialisierung kann wieder über die drei Wege
Beispiel:
int feld1[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
int feld2[3][4] = { {0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}, };
Wie bei eindimensionalen Arrays ist es auch hier möglich, die Elemente
fortlaufend in die geschweiften Klammern zu setzen. Bei einer Tabelle mit
wenigen Elementen ist dies noch leicht überschaubar. Steigt die Zahl
der Elemente aber, so bietet sich die zweite Variante an, mit der auch
feld2 initialisiert wurde. Hier lässt sich leicht ablesen,
welche Werte die Elemente der Zeilen und Spalten annehmen.
Struktur
Eine Struktur ist eine Folge von einem oder mehreren Elementen, die inhaltlich zusammengehören und über den Strukturnamen und den Elementnamen ansprechbar sind. In der Deklaration einer Struktur muss das Schlüsselwort struct erscheinen. Die einzelnen Elemente einer Struktur müssen nicht vom selben Typ sein, es sind auch komplexere Elemente wie Strukturen oder Arrays als Elemente einer Struktur zugelassen. Um die einzelnen Elemente anzusprechen gibt es zwei Wege. Zum einen kann ein Element über den Punkt (Bezeichner) angesprochen werden. Dazu muss der Name der Struktur und des Elementes, beide durch einen Punkt getrennt, eingegeben werden. Die andere Möglichkeit besteht darin die einzelnen Elemente über den Pfeil (Zeiger) anzusprechen.
Für die Deklaration einer Struktur gibt es verschiedene Möglichkeiten. Hierbei muss zwischen Strukturmuster, das den Aufbau der Struktur angibt und keinen Speicherplatz benötigt, und der eigentlichen Struktur unterschieden werden.
Beispiel 1:
struct
{
char nach_name[20];
char vor_name[20];
char strasse[20];
char ort[20];
} adresse;
strcpy (adresse.nach_name, "Meier");
strcpy (adresse.vor_name, "Emil");
In Beispiel 1 wird der Struktur der Name adresse zugewiesen.
Über die Funktion strcpy wird dem Element nach_name
der Name "Meier" zugeordnet.
Beispiel 2:
/* Ohne Strukturmuster */
struct
{
char name[20];
long verdienst;
} akte; /* Definition einer Struktur
akte */
Der Struktur wird der Name akte, über den sie dann
auch angesprochen wird, zugeordnet.
Beispiel 3:
/* Strukturmuster und Struktur */
struct AKTE
{
char name[20];
long verdienst;
};
struct AKTE akte; /* Definition der
Struktur */
Zunächst wird ein Strukturmuster mit dem Namen AKTE
definiert. Durch das Strukturmuster wird kein Speicherplatz belegt; die
eigentliche Zuordnung des Strukturnamens erfolgt erst in der letzten Zeile.
Beispiel 4:
struct AKTE
{
char name[20];
long verdienst;
} akte;
Dieses Beispiel zeigt, dass es durchaus möglich ist, ein Strukturmuster
zu definieren, und gleichzeitig eine Struktur zu erzeugen.
Auf die Elemente einer Struktur sind alle Operationen erlaubt, die sonst
für normale Datenobjekte gelten.
Union
Eine Sonderform des Datentyps Struktur ist der zusammengesetzte Datentyp Verbund oder union. Im Unterschied zum Datentyp Struktur werden hier die Elemente eines Verbundes an ein und der selben Adresse im Speicher abgelegt. Bei der Deklaration einer union braucht deshalb nur soviel Speicher reserviert werden, wie die größte Komponente in Anspruch nimmt.
Verbunde finden dann Anwendung, wenn es darum geht, ein Datenobjekt
unter unterschiedlichen Elementnamen anzusprechen. Die Deklaration erfolgt
wie die einer Struktur.
Beispiel:
struct
{
char fname[20];
char vname[20];
char strasse[20];
union
{
char plz[4];
char ort[20];
} ortsangabe;
} adresse;
/* Zugriff */ strcpy(adresse.ortsangabe.plz, "01167");
printf("\n%s\n", adresse.ortsangabe.plz);
strcpy(adresse.ortsangabe.ort, "01167 Dresden");
printf("%s\n", adresse.ortsangabe.ort);
Der Zugriff auf einen Verbund erfolgt ebenso wie bei Strukturen über den Punkt bzw. den Pfeil. Der Inhalt von plz ist vollständig in ort enthalten: er belegt die ersten 4 Bytes. Wird der sizeof Operator auf einen Verbund angewandt, so liefert er als Ergebnis den Wert der längsten Komponente.
Für die Deklaration und die zulässigen Operationen eines Verbundes
gelten die selben Regeln wie für die Struktur.
typedef
Die Programmiersprache C bietet dem Benutzer die Möglichkeit, mit dem Schlüsselwort
typedef eigene Datentypen zu definieren. Die Schaffung solcher eigenen Datentypen dient dabei im Wesentlichen dazu, die Lesbarkeit der Programme zu steigern. Durch das Schlüsselwort typedef werden zunächst Synonyme erzeugt, also gleichbedeutende Datentypen. Diese Synonyme können dann genauso verwendet werden, wie die von C vorgegebenen.
Am häufigsten werden selbstdefinierte Datentypen dazu verwendet,
zusammengesetzte Datenstrukturen wie Arrays oder Strukturen zu bezeichnen.
Beispiel 1:
typedef int Ganze_Zahl;
typedef char Zeichen;
Ganze_Zahl a = 12;
Zeichen b;
b = 'e';
Im aufgeführten Beispiel werden zunächst die Typen Ganze_Zahl (vom Typ int) und
Zeichen (vom Typ char) definiert. Danach
wird die Variable a mit dem Typ Ganze_Zahl vereinbart
und mit dem Wert 12 initialisiert. Die Variable b ist vom Typ Zeichen
(also char) und erhält in der letzten Zeile das Zeichen 'e'.
Beispiel 2:
typedef struct AKTE
{
char name[20];
long verdienst;
} Meine_Akte;
Meine_Akte akte;
In diesem Beispiel ist die Definition einer Struktur mit Hilfe von typedef
aufgezeigt. Zunächst wird das Strukturmuster mit dem Namen Akte
erzeugt. Der neue Datentyp bekommt die Bezeichnung Meine_Akte.
Im Anschluss daran wird die Struktur akte mit dem neuen Datentyp
Meine_Akte deklariert.
Datentyp enum
Mit dem Schlüsselwort enum lässt sich ein Aufzählungsdatentyp
deklarieren. Dieser ermöglicht es, in einer Liste all die Werte aufzuführen,
die eine Variable diesen Typs annehmen kann. Dabei werden die Werte nicht
als Zahlen, sondern als Namen aufgeführt. Dies hat den Vorteil, das
Programme besser lesbar werden.
Die Definition eines solchen Aufzählungstyps ähnelt der einer
Struktur. Zuerst erscheint das Schlüsselwort enum, gefolgt
von einem Namen. Die geschweiften Klammern umschließen die Liste.
In der Liste können nun Namen aufgeführt werden, denen auch Werte
zugewiesen werden können. Fehlen Wertzuweisungen, verteilt der Compiler
Werte für die Namen. Begonnen wird bei der 0. Der erste Name bekommt
also die 0, der zweite die 1, der dritte die 2 usw. Den Namen wird stets
der um 1 erhöhte Wert seines Vorgängers zugewiesen. Der Programmierer
kann an jeder beliebigen Stelle eingreifen, und eigene Werte vergeben.
Hinter der schließenden geschweiften Klammer kann ein Name für
die Variable dieses Aufzählungstyps folgen. Es ist aber auch später
möglich, diesem Aufzählungstypen Variablen zuzuweisen.
Beispiel:
enum tag
{
montag = 1,
dienstag,
mittwoch,
donnerstag,
freitag,
sonnabend,
sonntag = 0
};
enum tag wochentag;
In diesem Beispiel wird ein Aufzählungstyp mit dem Namen tag definiert. Die Namen in der Liste werden fortlaufend numeriert. Begonnen wird mit montag, der den Wert 1 zugewiesen bekommt, dienstag den Wert 2, usw. Dem Namen sonntag wird der Wert 0 zugeordnet. In der letzten Zeile wird mit dem Aufzählungstyp tag eine Variable mit dem Namen wochentag vereinbart.
Bei der Wahl der Namen in der Liste ist darauf zu achten, dass alle
Namen nur einmal vergeben werden.