4. Grundlagen

4.1 Elemente des Quellencodes
4.2 Datentypen
4.3 Deklarationen
4.4 Konstanten

4.4.1 Ganzzahlkonstanten

4.4.2 Gleitkommakonstanten

4.4.3 Zeichenkonstanten

4.4.4 Zeichenkettenkonstanten

4.5 Operatoren

4.5.1 Arithmetikoperatoren

4.5.2 Vergleiche und logische Verknüpfungen

4.5.3 Inkrement- und Dekrementoperatoren

4.5.4 Bitmanipulationen

4.5.5 Bedingte Bewertung

4.6 Ausdrücke und Zuweisungen

4.6.1 Operatorrangfolge

4.6.2 Zuweisungen

4.6.3 Datentypwandlungen

4.6.4 Zusammengesetzte Operatoren

4.1 Elemente des Quellencodes

C Programme bestehen aus "Worten": Schlüsselwörtern, (Objekt)Namen, Konstanten, Strings, Operatoren und anderen Trenn- und Sonderzeichen. Trennzeichen sind BLANK, TAB und Zeilenende. Kommentare wirken auch als Trennzeichen. Der Quellentext ist (fast) formatfrei.

Der Zeichenvorrat beinhaltet folgende Zeichen:

die Klein-und Großbuchstaben: a-z, A-Z

die Ziffern: 0-9

die Sonderzeichen: + - * / \ = , . ; ? " # % & _ '
< > ( ) [ ] { } | ^ ~ @ :

- die Umlaute (ä, ö, ü, Ä, Ö und Ü) sowie das "ß" gehören nicht zu den erlaubten Buchstaben

- Klein- und Großschreibung wird strikt unterschieden

- Schlüsselwörter müssen kleingeschrieben werden

- weiterhin ist folgende Konvention üblich:

  Variablennamen:         Kleinbuchstaben  Symbolische Konstanten: Großbuchstaben

- Anweisungen werden mit ; (Semikolon) abgeschlossen (Achtung: Das Vergessen des ; ist einer der häufigsten Anfängerfehler!!!)

- Schlüsselwörter sind:

auto break case char continue const

default do double else enum extern

float for goto if int long

register return short signed sizeof static

struct switch typedef union unsigned void

while volatile

- oft werden auch noch:

asm fortran near far huge pascal

- verwendet

- Bezeichner sind Namen von Objekten (z.B. Variablen- oder Funktionsnamen). Sie dürfen aus Buchstaben, Ziffern und dem Zeichen Underline "_" bestehen, sie müssen mit einem Buchstaben oder Underline anfangen (Hinweis: Namen, die mit Underline anfangen, werden oft auf Systemebene verwendet, daher eigene Namen besser ohne führendes Underline.)

- Variablennamen dürfen je nach C-Compiler unterschiedlich lang sein, man sollte sich aber bei Namen, die als Querbezug zu Programmen in anderen Quellendateien dienen, auf maximal 6 Zeichen beschränken. (Der Linker ist hier die beschränkende Komponente.) Namen, die nur programmintern auftreten haben meist 31 signifikante Stellen.

- zum Zwecke der internen Dokumentation sollten Variablennamen ihren Zweck und ihre Bedeutung erkennen lassen

- Beispiele für gültige Namen (links) und ungültige bzw. nicht zu empfehlende Namen (rechts):

prog1                 1_label        /* falsch */
was_nun               heutegehenwir  /* ungünstig bei 
                                         externen Bezügen */
punkte                wer.ist        /* falsch */
vorsicht             _vorsicht      /* ungünstig */
                      if             /* falsch */

- Kommentar kann überall dort stehen, wo auch sonst ein Trennzeichen stehen darf, er muß mit der Zeichenkombination /* eingeleitet und mit */ abgeschlossen werden (Natürlich darf Kommentar über mehrere Zeilen gehen.)

- sinnvollerweise schreibt man Kommentar an das Ende einer Zeile nach einer Anweisung oder auf eine eigene Zeile

- ein # in der 1.Spalte dient als Kennung für eine nachfolgende Präprozessoranweisung, diese werden vor der eigentlichen Kompilation ausgewertet, besonders wichtig sind hier:

a) das Einfügen von (Header)Dateien mittels:

#include "stdio.h"  /* Suche beginnt im Homeverzeichnis */
#include <stdio.h>  /* Suche beginnt im "include"	                      Verzeichnis*/

b) und die Definition von symbolischen Konstanten, die dann vor der eigentlichen Kompilation mittels Textersatz eingesetzt werden:

#define MAXIMUM 100

(das Wort "MAXIMUM" wird durch den Text "100" ersetzt

- Präprozessoranweisungen werden nicht mit Semikolon abgeschlossen, da dieses sonst beim Textersatz mit eingefügt wird

zurück zum Inhaltsverzeichnis

4.2 Datentypen

In C gibt es 4 Grunddatentypen; diese sind:

char    ein Zeichen    (meist 1 Byte, z.B. ASCII-Code)
int     ganze Zahl     (2 oder 4 Byte je nach Rechner)
float   Gleitpunktzahl (meist 4 Byte je nach Rechner)
double  Gleitpunktzahl (meist 8 Byte je nach Rechner)

Der Wertebereich für diese Grunddatentypen ist rechnerabhängig.

Diese Grunddatentypen können durch Voranstellen von "Eigenschaften" noch qualifiziert werden. Wird nur eine Eigenschaft angegeben, so wird als Typ int angenommen. Die möglichen Eigenschaften sind:

unsigned                     für char
unsigned, long und short     für int 
long                         für float

Beispiele:

unsigned int
long int
short int
unsigned char
unsigned short int
long
unsigned
long float

zurück zum Inhaltsverzeichnis

4.3 Deklarationen

Alle Variablen müssen vor ihrer Verwendung deklariert werden. Die Deklaration legt den Namen, den Typ und die Speicherklasse der Variablen fest. Deklarationen müssen am Anfang eines Blockes, am Anfang einer Funktion (Parameter) oder außerhalb von Funktionen (globale Variablen) stehen.

Variablentypen haben wir gerade in Punkt 4.2 kennengelernt. Speicherklassen legen fest, wie die Variable behandelt werden soll, insbesondere in welchem Speicherbereich sie angelegt werden soll. Es gibt 4 Speicherklassen:

auto
static
extern
register

auto ist sozusagen die Normalbehandlung und folglich auch der Weglasswert für Variablen, die in Blöcken deklariert werden. Der Weglasswert außerhalb von Funktionen ist extern zur Erzeugung globaler Variablen. static Variablen in Funktionen behalten ihren Wert zwischen 2 Funktionsaufrufen bei, auto Variablen werden jeweils neu angelegt.

Eine Besonderheit in C ist die Speicherklasse register. Damit kann man erreichen, daß Variablen (sofern es die Hardware des Rechners erlaubt) in CPU-Registern gehalten werden, was zu einer schnelleren Programmausführung führt.

Beispiele für Deklarationen:

auto int i;
static int i;
auto punkte;
auto char c;
char c;
register j;
register int i,j;

Gleichzeitig mit der Deklaration kann man die Variablen auch initialisieren:

auto int i=1;
static int i=0;
register int i=1,j=100;

Hinweis: Eine eingehendere Besprechung der 4 Speicherklassen wird im Zusammenhang mit Funktionen im Kapitel 6.4 vorgenommen.

zurück zum Inhaltsverzeichnis

4.4 Konstanten

4.4.1 Ganzzahlkonstanten

Ganzzahlkonstanten bestehen aus einer Ziffernkette. Sie werden mormalerweise dezimal interpretiert. Eine führende 0 führt zu einer oktalen Interpretation und die führende Zeichenkombination 0x oder 0X gilt als Kennung für Hexadezimalzahlen. Dezimalzahlen werden als normale int-Typen behandelt, Oktal- und Hexadezimalzahlen als unsigned.

Die folgenden Zahlenangaben sind zeilenweise jeweils gleich:

1 01 0x1

10 012 0xa

4368 010420 0x1110

Konstanten vom Typ long werden implizit gebildet, wenn die Zahl für int zu groß ist. Durch Anhängen von l oder L an die Konstante kann man eine long-Konstante erzwingen.

35000 0104270 0x8868

35000l 0104270l 0x8868l

10l 012l 0xal

4.4.2 Gleitkommakonstanten

Sie haben immer den Typ double. Der Dezimalpunkt und/oder die Exponentenangabe e oder E muß vorhanden sein, so wie man das gewohnt ist:

.1234

0.1234

1.345e-10

3E+3

4.4.3 Zeichenkonstanten

Sie haben den Typ char und werden durch Angabe eines Zeichens eingeschlossen in (einfache) Anführungszeichen erzeugt. Ihr Wert ist der numerische Wert des Zeichens im gültigen Zeichensatz (z.B. ASCII).

'a'     /* numerischer Wert 97  (0x61) */
'F'     /* numerischer Wert 70  (0x46) */
'1'     /* numerischer Wert 49  (0x31) */

Nicht druckbare Zeichen können mit Hilfe des Fluchtsymbols \ (Backslash) nach folgender Tabelle angebeben werden:

Backspace              BS    \b
Tabulator Zeichen      HT    \t
Zeilentrenner          LF    \n
Formfeed               FF    \f
Carriage Return        CR    \r
Fluchtsymbol           \     \\
Anführungszeichen      '     \'
Null Zeichen           NUL   \0
Bitmuster              ddd   \ddd

ddd steht dabei für maximal 3 Oktalziffern, damit ist jedes beliebige Zeichen des Zeichensatzes darstellbar. Auch die Fluchtsymbolfolgen müssen natürlich in Anführungszeichen eingeschlossen werden.

4.4.4 Zeichenkettenkonstanten

Zeichenkettenkonstanten bestehen aus einer linearen Folge von Einzelzeichen mit einem wichtigen Unterschied: Am Ende einer Zeichenkette wird immer ein NUL-Zeichen ('\0') als Endekennung automatisch angehängt. Zeichenkettenkonstanten werden in doppelte Anführungszeichen eingeschlossen. Es gelten die gleichen Fluchtsymbolfolgen wie bei Zeichenkonstanten; zusätzlich gibt es noch die Fluchtsymbolfolge \". Zeichenketten können über Zeilengrenzen gehen, wenn unmittelbar vor dem Zeilentrenner (im Quellencode) das Fluchtsymbol \ steht.

'A'                  /* das Zeichen A */
"A"                  /* die Zeichenkette A\0 */
"Dies ist eine Zeichenkette"
"\"Hahaha\" lachte er"
"Diese Zeichenkette piepst \07"

zurück zum Inhaltsverzeichnis

4.5 Operatoren

4.5.1 Arithmetikoperatoren

Es gibt 6 arithmetische Operatoren. Sie stehen für die 4 Grundrechenarten (+ - * /), die Modulofunktion (Rest nach Division; % ) und die arithmetische Negation (das Vorzeichen; -).

Vorzeichen             -Multiplikation         *Division               /Modulofunktion         %Addition               +Subtraktion            -

Die Modulofunktion ist nicht auf float oder double Operanden anwendbar. Die Rangfolge ist wie gewohnt: *, / und % gleich und höher als + und -. Das Minuszeichen als unärer Operator (Vorzeichen) hat einen höheren Vorrang als *, / und %. Es gibt kein positives Vorzeichen.

Beispiele für die Anwendung arithmetischer Operatoren:

int i=3, j=5, k=10;
int m;
float r=3., s=5.;
float u;
m = i+j+k;              /* 18 */
m = k-j-i;              /*  2 */
m = i-k;                /* -7 */
m = k/i;                /*  3 */
m = k/i+j;              /*  8 */
m = k/(i+j);            /*  1 */
m = k*i+j;              /* 35 */
m = k*(i+j);            /* 80 */
m = k%i;                /*  1 */
u = s+r;                /* 8.       */
u = s/r;                /* 1.666... */
u = s*r;                /*15.       */
u = k/r;                /* 3.333... */
u = k/r +s/r;           /* 5.       */
u = k/i + s/i;          /* 4.666... */
u = k%r;                /* nicht erlaubt */

Höhere Rechenoperationen wie Wurzelziehen, Potenzieren, Exponentiation, usw. fehlen in C als Operator ganz; sie sind über Bibliotheksfunktionen realisiert. Bei ihrer Verwendung muß in jedem Fall die Inlcude-Datei math.h mit eingebunden werden.

Beispielprogramm P4-1.C

#define LAUFZEIT 10
#define ANF_KAP 1000.
#define ZI_SATZ 2.5
main(){
int i;
float anf_kap,end_kap;
printf("\nGuthabenzinsenberechnung\n");
printf("Jahr Anfangskapital Endkapital\n");
anf_kap=ANF_KAP;
for (i=1; i <= LAUFZEIT; i++) {
   end_kap = anf_kap + ZI_SATZ/100 * anf_kap;
   printf("%2d    %8.2f      %8.2f\n",i,anf_kap,end_kap);
   anf_kap = end_kap;
}
}

4.5.2 Vergleiche und logische Verknüpfungen

Für Vergleiche stehen folgende Operatoren zur Verfügung:

kleiner              <kleiner gleich       <=größer               >größer gleich        >=gleich (identisch)   ==ungleich             !=

Die ersten 4 haben untereinander gleichen Rang, stehen aber eine Rangstufe höher als die beiden letzten. Es gibt in C grundsätzlich keinen Datentyp BOOLEAN; WAHR oder FALSCH werden einfach über den numerischen Wert entschieden. Dabei gilt:

ungleich  0     WAHR (erhält den Wert eins)
  gleich  0     FALSCH

Beispiele für die Verwendung dieser Operatoren:

int a=5;
int b=12;
int c=7;
   a < b                /* WAHR   */
   b < c                /* FALSCH */
   a+c <= b             /* WAHR   */
   b-a >= c             /* WAHR   */
   a == b               /* FALSCH */
   a+c == b             /* WAHR   */
   a != b               /* WAHR   */
   a = b<c;             /* möglich: a=0 */
   a = c<b;             /*   -"-    a=1 */

Unter die logischen Verknüpfungen fallen die Operatoren:

logische Negation    !logisches UND        &&logisches ODER       ||

Sie sind hier in ihrer Rangfolge geordnet aufgeführt, logisches UND hat also einen höheren Vorrang als das logische ODER. In Ausdrücken erfolgt die Abarbeitung von links nach rechts aber nur solange, bis das Ergebnis eindeutig feststeht. Das führt zu einer kürzeren Ausführungszeit, wenn man die häufigst verwendete Bedingung an den Anfang einer Bedingungsabfrage stellt. Bei Operationen, die Nebeneffekte haben können (z.B. Inkrementation), muß man allerdings vorsichtig sein.

if (a<b            &&  (d=(a+b)) != c)   /* diese 2 Zeilen */
if ((d=(a+b)) != c &&  a<b)         /* wirken nicht gleich */

4.5.3 Inkrement- und Dekrementoperatoren

C kennt spezielle Operatoren zum In- und Dekrementieren. Es sind dies:

Inkrement           ++Dekrement           --

Sie sind (wie der Vorzeichenoperator -) rechts assoziativ, werden also von rechts her zusammengefaßt. Sie können vor oder nach ihrem Operanden stehen. Im ersten Fall wird der Operand zunächst in- oder dekrementiert und dann weiterverwendet, im zweiten Fall wird der Operand erst verwendet und dann in- oder dekrementiert.

int i;
int k;
i=1;
if (++i > 1)
    k = 5;            /* wird durchlaufen */
if (i++ > 2)
    k = 10;           /* wird nicht durchlaufen */
printf ("\nk hat den Wert %d\n",k);    /* Ausgabe: 5 */
int i,k;
int j;
i=2;
k=3;
j = i+++k;            /* i+ (++k); i=2,k=4,j=6  */
j = (i++)+k;          /*           i=3,k=4,j=6  */
j = i++ +k;           /*           i=4,k=4,j=7  */
j = (i+k)++;          /* ist verboten           */

Beispielprogramm P4-2.C:

strcat(s,t)      /* String t wird an String s angehängt */
char s[],t[];
{
int i,j;
   i=j=0;
while (s[i] != '\0')
      i++;
while ((s[i++]=t[j++]) != '\0')
      ;
}

Beispielprogramm P4-3.C:

squeeze(s1,s2)   /* Entfernt aus String s1 alle Zeichen, */
                 /* die in String s2 vorkommen */
char s1[],s2[];
{
int i,j,k;
for (i=j=0; s1[i] != '\0'; i++) {
      s1[j++] = s1[i];
for (k=0; s2[k] != '\0'; k++)
if (s1[i] == s2[k]) {
            j--;
break;
         }
   }
   s1[j] = '\0';
   return (j);     /* Anzahl Zeichen im String */
}

Beispielprogramm P4-4.C:

any(s1,s2)    /* Gibt die Position des ersten Zeichens in 
                 s1 an, das in s2 enthalten ist */
char s1[],s2[];
{
int i,j,n;
for (i=0,n=-1; s1[i]!='\0' && n==-1; i++)
for (j=0; s2[j] != '\0'; j++)
if (s1[i] == s2[j]) {
            n = i;
break;
         }
return (n);
}

4.5.4 Bitmanipulationen

C stellt auch Operatoren für Bitmanipulationen zur Verfügung. Sie können nicht auf Variablen oder Konstanten vom Typ float oder double angewendet werden. Die Operatoren sind wie folgt:

Komplement                  ~Linksshift                 <<Rechtsshift                >>bitweises UND               &bitweises EXCLUSIVES ODER   ^bitweises ODER              |

Beispiele:

int i=7;
int j=9;
int k;
k = i & j;        /* k=1   */
k = i | j;        /* k=15  */
k = i ^ j;        /* k=14  */
k = ~0            /* k=max(unsigned int) */
k = ~i;           /* k=max(unsigned int) -7 */
k = i << 3;       /* k=56  */
k = i >> 3;       /* k=0   */
k = i & 0x03;     /* k=3; alle Bits bis auf die beiden */
                  /* unteren ausmaskiert */

Beispielprogramm P4-5.C:

getbits(x,p,n)  /* n Bits ab Position p rechtsbündig */
                /* ... 7 6 5 4 3 2 1 0               */
unsigned x;
int p,n;
{
return ((x >> (p+1-n)) & ~(~0<<n));
}

Beispielprogramm P4-6.C:

wordlength()    /* Bestimmt die Bitlänge von int */
{
int i,j;     /* oder: unsigned i; int j; */
   i = ~0;
   j = 0;
while (i) {
      j++;
      i = i << 1;
   }
return (j);
}

Beispielprogramm P4-7.C:

bitcount(n)  /* zählt die Anzahl der 1 Bits in n */
unsigned int n;
{
int b;
for (b=0; n!=0; n >>= 1)
if (n & 01)
         b++;
return (b);
}
bitcount(n)  /* 2. schnellere Version */
unsigned int n;
{
int b;
   b=0;
   while (n != 0) {
      b++;
      n = n & (n-1);
   }
return (b);
}

4.5.5 Bedingte Bewertung

C kennt einen Operator, der eigentlich aus einem Operatorenpaar (? :), besteht. Es handelt sich dabei um die bedingte Bewertung. Sie hat die Form:

Ausdruck1 ? Ausdruck2 : Ausdruck3

Ist Ausdruck1 WAHR, dann wird Ausdruck2 ausgeführt, ansonsten Ausdruck3. Die bedingte Bewertung entspricht daher folgender if-else Konstruktion:

if (Ausdruck1)
   Ausdruck2;
else   Ausdruck3;

Beispiele:

/* Maximum von a und b */
z = (a > b) ? a : b;
/* Ausgabe eines Feldes, wobei immer nach 10 Zahlen
   eine neue Zeile angefangen werden soll */
for (i=0; i < N; i++)
   printf ("%6d%c",a[i],(i%10==9||i==N-1)?'\n':' ');

zurück zum Inhaltsverzeichnis

4.6 Ausdrücke und Zuweisungen

Ein Ausdruck besteht aus Operanden und Operatoren oder einem Funktionsaufruf. Ein Ausdruck gefolgt von einem Semikolon ist eine Anweisung. Zur Konstruktion gültiger Ausdrücke bzw. Anweisungen sind noch einige Dinge zu klären, die bisher noch nicht besprochen wurden.

4.6.1 Operatorrangfolge

Bis zu diesem Zeitpunkt haben wir die meisten Operatoren besprochen. Zu ihrer richtigen Anwendung gehört nicht nur das Wissen um ihre Funktion, sondern auch das um ihre Rangfolge und ihre Assoziativität. Diese Angaben sind in der folgenden Tabelle für alle Operatoren zusammengestellt, auch für die bisher noch nicht besprochenen. Die Operatoren in der Tabelle sind ihrer Rangfolge nach von oben (höchster Rang) nach unten (niedrigster Rang) angeordnet. Alle Operatoren, die auf einer Zeile stehen haben gleiche Rangfolge und können bei einem gemeinsamen Auftreten in einem Ausdruck vom Compiler in beliebiger Reihenfolge bewertet werden.

Operator                            Zusammenfassung
                                    (Assoziativität)()  []  ->  .                       von links her
!  ~  ++ --  - (typ)  *  &  sizeof  von rechts her
*  /  %                             von links her
+  -                                von links her
<<  >>                              von links her
<  <=  >  >=                        von links her
==  !=                              von links her
&                                   von links her
^                                   von links her
|                                   von links her
&&                                  von links her
||                                  von links her
?:                                  von rechts her
=  +=  -=  += (etc.)                von rechts her
,                                   von links her

Auf folgende Dinge wird hier noch einmal besonders hingewiesen:

- es werden teilweise die gleichen Operatorzeichen für verschiedene Operatoren verwendet

()   1. Klammerung    2. type cast
*    1. Pointerdekl.  2. Multiplikation
-    1. Vorzeichen    2. Subtraktion
&    1. Adresse von   2. bitweises UNDD

- In- und Dekrementoperatoren haben einen sehr hohen Vorrang

- die Arithmetikoperatoren haben einen Vorrang wie erwartet und gewohnt

- Shifts haben einen höheren Vorrang als Vergleiche, im Gegensatz zu den anderen Bitmanipulationen, die nach den Vergleichen stehen

- bitweise logische Operationen stehen vor den logischen Operationen, die sich auf ganze Zahlen beziehen

- die bedingte Bewertung hat einen sehr niedrigen Vorrang, trotzdem sollte man sich bei ihrer Anwendung eine Klammerung angewöhnen, damit die Anweisung leichter lesbar ist.

- außer durch den Vorrang ist die Reihenfolge der Bewertung undefiniert: z = (a*2) + ++a kann als (a*2)+ ++a aber auch als ++a + (a*2) bewertet werden. Man nennt dies einen Nebeneffekt. Diese müssen aus naheliegenden Gründen vermieden werden.

- ebenso ist die Reihenfolge der Bewertung von Funktionsparametern nicht gewährleistet:

i=1;
printf("%d %d",++i,i*3)   /* kann 2,6 oder 4,3 ausgeben */

- ebenfalls nicht eindeutig definiert ist folgende Konstruktion:

a[i]=i++;

4.6.2 Zuweisungen

- die linke Seite einer Zuweisung muß ein einfacher Ausdruck und ein sogenannter L-Wert sein

- einfache Ausdrücke bezeichnen ein einzelnes Objekt, dieses Objekt kann eine Variable, eine Konstante, ein Funktionsaufruf, oder ein Feld-, Struktur-, Aufzählungs- oder Variantenelement sein.

- L-Werte sind Objekte, die veränderbar sind z.B. Variablen

- keine L-Werte sind: Konstanten, Feldnamen ohne folgende Indexangabe, Funktionsnamen, Namen aus einer Aufzählung, Objekte, die explizit mit const als konstant vereinbart wurden

- Zuweisungen können in Ausdrücken an der Stelle eines Operanden stehen (z.B.: while ((c=getchar()) != EOF)

- Zuweisungen in einer Zuweisungskette werden von rechts nach links ausgeführt:

a = b = c = 1;     /* alle 1 */

- mit Hilfe des Kommaoperators können Listen von Anweisungen gebildet werden, sie werden von links nach rechts abgearbeitet

a=b, b=c, c=1;    /* nur c=1, andere unbestimmt */
c=1, b=c, a=b;    /* alle 1 */

- Ausdruckslisten werden oft im Zusammenhang mit einer for-Schleife verwendet, um z.B. in der Reinitialisierung 2 verschiedene Operationen durchzuführen:

for (i=0,j=10;i < N; i++,j++)

- Vorsicht ist allerdings dann geboten, wenn das Komma wie z.B. in einer Funktionsparameterliste noch eine andere Funktion hat, durch eine Klammerung kann man aber jederzeit das Gewünschte erreichen:

printf("Was wird hier %d ausgeben ?",(i=1,i=i+10));

4.6.3 Datentypwandlungen

- Datentypwandlungen sind immer dann notwenig, wenn 2 Operanden in einem Ausdruck verschiedene Typen haben

- Datentypwandlungen werden soweit notwendig implizit durchgeführt

- char wird bei Bewertungen immer in int gewandelt, dadurch sind int und char beliebig mischbar

- allerdings kann die char-int Wandlung rechnerabhängig sein, char kann auf die Zahlenwerte -128 bis +127 oder 0 bis +255 abgebildet sein, die "normalen" Zeichen liegen aber immer im positiven Bereich

- die char-int Wandlung ist vor allem bei der Abfrage eines eingelesenen Zeichens auf EOF wichtig (Eingelesen werden immer int Werte, die dann aber einfach char Werten zugewiesen werden können

- die Wandlung von int nach long erfolgt durch Vorzeichenerweiterung (bei signed) oder Voranstellen von Nullen (unsigned), bei der umgekehrten Wandlung werden die höchstwertigen Bits einfach abgeschnitten

- float wird bei Bewertungen immer in double gewandelt, alle Rechnungen erfolgen daher mit derselben großen Genauigkeit

- die Wandlung von int nach float ist wohldefiniert, die umgekehrte Richtung kann rechnerabhängig sein, insbesondere bei negativen Gleitkommazahlen

- den genauen Ablauf der Wandlungen bei der Auswertung eines Ausdrucks oder einer Zuweisung beschreiben folgende Regeln (übliche arithmetische Umwandlungen):

zuerst werden char oder short Operanden in int Werte und float Operanden in double Werte umgewandelt

hat jetzt ein Operand den Typ double, so wird der andere ebenfalls in double gewandelt und das Resultat ist auch double

ist stattdesssen einer der Operanden long, so wird der andere auch in long gewandelt und das Resultat ist auch long

ist andernfalls einer der Operanden unsigned, so wird auch der andere in unsigned gewandelt und das Resultat ist ebenfalls unsigned

liegt keiner dieser Fälle vor, so handelt es sich um int Werte, diesen Typ hat dann natürlich auch das Resultat

- durch eine sogenannte type cast Operation kann man in einem Ausdruck den Typ eines Operanden auf den geünschten Typ abändern, dies Operation ändert den Typ nur für diese eine Operation:

int n;
float r;
r=sqrt((double)n);    /* sqrt benötigt double Parameter */

- einige Beispiele für implizite Typwandlungen haben wir schon in Kapitel 4.5.1 bei den Arithmetikoperatoren kennengelernt, hier sind noch einige mehr, insbesondere zur char-int Wandlung

Beispielprogramm P4-8.C:

atoi(s)     /* wandelt die nächsten x Ziffern aus dem
               String s in eine Integerzahl */
char s[];
{
int i,n;
   n=0;
for (i=0; s[i]>='0' && s[i]<='9'; i++)
      n = 10*n + s[i]-'0';
return (n);
}

Beispielprogramm P4-9.C:

htoi(s)     /* wie atoi, aber für Hexadezimalzahlen */
char s[];
{
int i,n;
   n=0;
for (i=0; ; i++)
if (s[i] >= '0' && s[i] <= '9')
         n = 16*n + s[i]-'0';
else if (s[i] >= 'A' && s[i] <= 'F')
         n = 16*n + s[i]-'A'+10;
else if (s[i] >= 'a' && s[i] <= 'f')
         n = 16*n + s[i]-'a'+10;
else 
break;
return (n);
}

4.6.4 Zusammengesetzte Operatoren

-- Eine Spezialität von C ist die abgekürzte Schreibweise für bestimmte Zuweisungen

-- diese abgekürzte Schreibweise ist vorteilhaft, wenn die linke Seite eine komplizierte Struktur hat (weniger Tippfehler, bessere Lesbarkeit)

-- bei der abgekürzten Schreibweise gilt folgendes:

Ausdruck1  op= Ausdruck2

ist äquivalent zu (beachten Sie die Klammern!):

Ausdruck1  =  (Ausdruck1) op (Ausdruck2)

- op kann einer der Operatoren: + - * / % << >> & ^ oder | sein

- die Klammern sind sehr wichtig, damit keine unerwünschten Nebeneffekte auftreten:

i *= k+1;      /* wird wie */
i = i * (k+1); /* behandelt und nicht wie */
i = i * k +1;

- der Vorteil der abgekürzten Schreibweise wird an folgendem Beispiel deutlich:

feld[i*3+j-7] = feld[i*3+j-7] + 10;     /* normal */
feld[i*3+j-7] += 10;                    /* abgekürzt */

zurück zum Inhaltsverzeichnis