4.5.2 Vergleiche und logische Verknüpfungen
4.5.3 Inkrement- und Dekrementoperatoren
4.6.4 Zusammengesetzte Operatoren
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
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
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.
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
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
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.
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"
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); }
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); }
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':' ');
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.
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++;
- 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));
- 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 */