Jedes C-Programm besteht aus verschiedenen Komponenten, die in bestimmter Weise kombiniert werden. Der größte Teil dieses Buches beschäftigt sich damit, diese Programmkomponenten zu erläutern und deren Einsatz zu zeigen. Für das Gesamtbild ist es hilfreich, wenn Sie sich zunächst ein vollständiges - wenn auch kleines - C- Programm ansehen, in dem alle Komponenten gekennzeichnet sind. Heute lernen Sie:
Listing 2.1 zeigt den Quellcode für das Programm multiplizieren.c
. Dieses sehr
einfache Programm übernimmt zwei Zahlen, die der Benutzer über die Tastatur
eingibt, und berechnet das Produkt der beiden Zahlen. Momentan brauchen Sie sich
noch keine Gedanken darum zu machen, wie das Programm im Detail arbeitet. Es
geht zunächst nur darum, dass Sie die Teile eines C-Programms kennen lernen, damit
Sie die später in diesem Buch präsentierten Listings besser verstehen.
Bevor Sie sich das Beispielprogramm ansehen, müssen Sie wissen, was eine Funktion ist, da Funktionen eine zentrale Rolle in der C-Programmierung spielen. Unter einer Funktion versteht man einen unabhängigen Codeabschnitt, der eine bestimmte Aufgabe ausführt und dem ein Name zugeordnet ist. Ein Programm verweist auf den Funktionsnamen, um den Code in der Funktion auszuführen. Das Programm kann auch Informationen - so genannte Argumente - an die Funktion übergeben, und die Funktion kann Informationen an den Hauptteil des Programms zurückgeben. In C unterscheidet man Bibliotheksfunktionen, die unter Linux Teil des Betriebssystems sind, und benutzerdefinierte Funktionen, die der Programmierer erstellt. Im Verlauf dieses Buches werden Sie noch mehr über beide Arten von Funktionen erfahren.
Beachten Sie, dass die Zeilennummern in Listing 2.1 wie bei allen Listings in diesem Buch nicht zum Programm gehören und nur für Verweise im laufenden Text vorgesehen sind. Geben Sie die Zeilennummern also nicht mit ein.
Listing 2.1: Das Programm multiplizieren.c multipliziert zwei Zahlen.
1: /* Berechnet das Produkt zweier Zahlen. */
2: #include <stdio.h>
3:
4: int a,b,c;
5:
6: int produkt(int x, int y);
7:
8: int main()
9: {
10: /* Erste Zahl einlesen */
11: printf("Geben Sie eine Zahl zwischen 1 und 100 ein: ");
12: scanf("%d", &a);
13:
14: /* Zweite Zahl einlesen */
15: printf("Geben Sie eine weitere Zahl zwischen 1 und 100 ein: ");
16: scanf("%d", &b);
17:
18: /* Produkt berechnen und anzeigen */
19: c = produkt(a, b);
20: printf ("%d mal %d = %d\n", a, b, c);
21:
22: return 0;
23: }
24:
25: /* Funktion gibt Produkt der beiden bereitgestellten Werte zurück */
26: int produkt(int x, int y)
27: {
28: return (x * y);
29: }
Geben Sie eine Zahl zwischen 1 und 100 ein: 35
Geben Sie eine weitere Zahl zwischen 1 und 100 ein: 23
35 mal 23 = 805
Die folgenden Abschnitte beschreiben die verschiedenen Komponenten des Beispielprogramms aus Listing 2.1. Durch die angegebenen Zeilennummern können Sie die jeweiligen Stellen schnell finden.
Die einzige Komponente, die in jedem ausführbaren C-Programm erforderlich ist,
aber nur einmal vorhanden sein darf, ist die Funktion main()
. In ihrer einfachsten
Form besteht diese Funktion nur aus dem Namen main
gefolgt von einem leeren
Klammernpaar (()
) und einem Paar geschweifter Klammern ({}
). Innerhalb der
geschweiften Klammern stehen die Anweisungen, die den Hauptrumpf des
Programms bilden. Unter normalen Umständen beginnt die Programmausführung bei
der ersten Anweisung in main()
und endet mit der letzten Anweisung in dieser
Funktion.
Die
#include
-Direktive weist den C-Compiler an, den Inhalt einer so genannten Include-Datei während der Kompilierung in das Programm einzubinden. Eine Include-Datei ist eine separate Datei mit Informationen, die das Programm oder der Compiler benötigt. Zum Lieferumfang des Compilers gehören mehrere dieser Dateien (man spricht auch von Header-Dateien). Diese Dateien müssen Sie nie modifizieren. Aus diesem Grund hält man sie auch vom Quellcode getrennt. Include-Dateien sollten die Erweiterung.h
erhalten (zum Beispielstdio.h
).
In Listing 2.1 bedeutet die #include
-Direktive: »Füge den Inhalt der Datei stdio.h
in
das Programm ein.« In den meisten C-Programmen sind eine oder mehrere Include-
Dateien erforderlich. Mehr Informationen dazu bringt Tag 20, »Compiler für
Fortgeschrittene«.
Eine Variable ist ein Name, der sich auf eine bestimmte Speicherstelle für Daten bezieht. Ein Programm verwendet Variablen, um verschiedene Arten von Daten während der Programmausführung zu speichern. In C muss man eine Variable zuerst definieren, bevor man sie verwenden kann. Die Variablendefinition informiert den Compiler über den Namen der Variablen und den Typ der Daten, die die Variable aufnehmen kann. Das Beispielprogramm aus Listing 2.1 definiert in Zeile 4 mit der Anweisung
int a,b,c;
drei Variablen mit den Namen a
, b
und c
, die jeweils einen ganzzahligen Wert
aufnehmen. Mehr zu Variablen und Variablendefinitionen erfahren Sie im Abschnitt
»Daten speichern: Variablen und Konstanten« weiter hinten in diesem Kapitel.
Funktionsprototypen teilen dem C-Compiler den Namen und die Argumente der im Programm vorkommenden Funktionen mit. Bevor eine Funktion im Programm verwendet werden kann, muss der Funktionsprototyp im Programm bekannt gemacht worden sein. Ein Funktionsprototyp ist nicht mit der Funktionsdefinition zu verwechseln. Die Funktionsdefinition enthält die eigentlichen Anweisungen, die die Funktion ausmachen. (Auf Funktionsdefinitionen geht die heutige Lektion weiter hinten ein.)
Die eigentliche Arbeit eines C-Programms erledigen die Anweisungen. Mit C-
Anweisungen zeigt man Informationen auf dem Bildschirm an, liest Tastatureingaben,
führt mathematische Operationen aus, ruft Funktionen auf, liest Dateien - kurz gesagt,
realisieren die Anweisungen alle Operationen, die ein Programm ausführen muss. Der
größte Teil dieses Buches erläutert Ihnen die verschiedenen C-Anweisungen. Fürs Erste
sollten Sie sich merken, dass man im Quellcode gewöhnlich eine Anweisung pro Zeile
schreibt und eine Anweisung immer mit einem Semikolon abzuschließen ist. Die
folgenden Abschnitte erläutern kurz die Anweisungen im Programm multiplizieren.c
.
Die Anweisung printf()
in den Zeilen 11, 15 und 20 ist eine Bibliotheksfunktion, die
Informationen auf dem Bildschirm ausgibt. Wie die Zeilen 11 und 15 zeigen, kann die
Anweisung printf()
eine einfache Textnachricht ausgeben oder - wie in Zeile 20 -
die Werte von Programmvariablen gemeinsam mit Text.
Die Anweisung scanf()
in den Zeilen 12 und 16 ist eine weitere Bibliotheksfunktion.
Sie liest Daten von der Tastatur ein und weist diese Daten einer oder mehreren
Programmvariablen zu.
Die Anweisung in Zeile 19 ruft die Funktion produkt()
auf, d.h. sie führt die
Programmanweisungen aus, die in der Funktion produkt()
enthalten sind. Außerdem
übergibt sie die Argumente a
und b
an die Funktion. Nachdem die Anweisungen in der
Funktion produkt()
abgearbeitet sind, gibt produkt()
einen Wert an das Programm
zurück. Diesen Wert speichert das Programm in der Variablen c
.
Die Zeilen 22 und 28 enthalten return
-Anweisungen. Die return
-Anweisung in Zeile
28 gehört zur Funktion produkt()
. Der Ausdruck in der return
-Anweisung berechnet
das Produkt der Werte in den Variablen x
und y
und gibt das Ergebnis an das
Programm zurück, das die Funktion produkt()
aufgerufen hat. Unmittelbar bevor das
Programm endet, gibt die return
-Anweisung in Zeile 22 den Wert 0 an das
Betriebssystem zurück.
Eine Funktion ist ein unabhängiger und selbstständiger Codeabschnitt, der für eine bestimmte Aufgabe vorgesehen ist. Jede Funktion hat einen Namen. Um den Code in einer Funktion auszuführen, gibt man den Namen der Funktion in einer Programmanweisung an. Diese Ausführung bezeichnet man als Aufrufen der Funktion.
Die Funktion mit dem Namen produkt()
in den Zeilen 26 bis 29 ist eine
benutzerdefinierte Funktion, die der Programmierer (d.h. der Benutzer der Sprache C)
während der Programmentwicklung erstellt. Die einfache Funktion in den Zeilen 26 bis
29 multipliziert lediglich zwei Werte und gibt das Ergebnis an das Programm zurück,
das die Funktion aufgerufen hat. Am Tag 4, »Funktionen«, lernen Sie, dass die richtige
Verwendung von Funktionen einen wichtigen Teil in der Programmierpraxis mit C
ausmacht.
In einem »richtigen« C-Programm wird man kaum eine Funktion für eine so einfache
Aufgabe wie die Multiplikation zweier Zahlen aufsetzen. Das Beispielprogramm
multiplizieren.c
soll lediglich das Prinzip verdeutlichen.
C umfasst auch Bibliotheksfunktionen, die Teil des Betriebssystems oder des C-
Compilerpakets sind. Bibliotheksfunktionen führen vor allem die allgemeinen
Aufgaben (wie die Ein-/Ausgabe mit Bildschirm, Tastatur und Festplatte) aus, die ein
Programm benötigt. Im Beispielprogramm sind printf()
und scanf()
Bibliotheksfunktionen.
Jeder Teil eines Programms, der mit den Zeichen
/*
beginnt und mit den Zeichen*/
endet, ist ein Kommentar. Da der Compiler alle Kommentare ignoriert, haben sie keinen Einfluss auf die Arbeitsweise des Programms. Man kann alles Mögliche in Kommentare schreiben, ohne dass es sich irgendwie im Programm bemerkbar machen würde. Ein Kommentar kann nur einen Teil einer Zeile, eine ganze Zeile oder auch mehrere Zeilen umfassen. Dazu drei Beispiele:/* Ein einzeiliger Kommentar */
int a, b, c; /* Ein Kommentar, der nur einen Teil der Zeile betrifft */
/* Ein Kommentar,
der sich über mehrere
Zeilen erstreckt. */
Achten Sie darauf, keine verschachtelten Kommentare zu verwenden. Unter einem verschachtelten Kommentar versteht man einen Kommentar, der innerhalb der Begrenzungszeichen eines anderen Kommentars steht. Die meisten Compiler akzeptieren keine Konstruktionen wie:
/*
/* Verschachtelter Kommentar */
*/
Manche Compiler lassen verschachtelte Kommentare zu. Obwohl es verlockend erscheint, sollte man jedoch auf verschachtelte Kommentare generell verzichten. Einer der Vorteile von C ist bekanntlich die Portabilität, und Konstruktionen wie zum Beispiel verschachtelte Kommentare können die Portabilität Ihres Codes einschränken. Darüber hinaus führen derartige Kommentarkonstruktionen oftmals zu schwer auffindbaren Problemen.
Viele Programmieranfänger betrachten Kommentare als unnötig und als reine Zeitverschwendung. Das ist ein großer Irrtum! Die Arbeitsweise eines Programms mag noch vollkommen klar sein, wenn Sie den Code niederschreiben. Sobald aber Ihr Programm größer und komplexer wird oder wenn Sie Ihr Programm nach sechs Monaten verändern müssen, stellen Kommentare eine unschätzbare Hilfe dar. Spätestens dann dürften Sie erkennen, dass man Kommentare großzügig einsetzen sollte, um alle Programmstrukturen und Abläufe zu dokumentieren.
Viele Programmierer haben sich einen neueren Stil der Kommentare in ihren C-Programmen zu eigen gemacht. In C++ und Java kann man Kommentare mit doppelten Schrägstrichen kennzeichnen, wie es die folgenden Beispiele zeigen:
// Das ist ein Kommentar, der sich über eine Zeile erstreckt
int x; // Dieser Kommentar macht nur einen Teil der Zeile ausDie Schrägstriche signalisieren, dass der Rest der Zeile ein Kommentar ist. Obwohl viele C-Compiler diese Form der Kommentare unterstützen, sollte man sie vermeiden, wenn die Portabilität des Programms zu wahren ist.
Mit den geschweiften Klammern {
und }
schließt man Programmzeilen ein, die eine
C-Funktion bilden - das gilt auch für die Funktion main()
. Eine Gruppe von einer oder
mehreren Anweisungen innerhalb geschweifter Klammern bezeichnet man als Block.
In den weiteren Lektionen werden Sie noch viele Einsatzfälle für Blöcke kennen lernen.
Nehmen Sie sich die Zeit, das Programm multiplizieren.c
einzugeben, zu
kompilieren und auszuführen. Es bringt Ihnen etwas mehr Praxis im Umgang mit
Editor und Compiler. Zur Wiederholung seien hier noch einmal die Schritte analog zu
Lektion 1, »Einführung in Linux und die Programmiersprache C«, genannt:
multiplizieren.c
genau wie in Listing 2.1 gezeigt
ein (außer den Zeilennummern mit Doppelpunkt).
./multiplizieren
an der Eingabeaufforderung ausführen.
Ein Computer arbeitet schnell und genau. Allerdings nimmt er alles wörtlich und er kann nicht einmal einfachste Fehler korrigieren. Er übernimmt daher alles genau so, wie Sie es eingegeben und nicht wie Sie es gemeint haben!
Das gilt ebenso für Ihren C-Quellcode. Ein einfacher Schreibfehler im Programm - schon beschwert sich der C-Compiler und bricht die Kompilierung ab. Auch wenn der Compiler Ihre Fehler nicht korrigieren kann (die auch Sie unweigerlich machen werden), so ist er doch zum Glück so intelligent, dass er Fehler erkennt und meldet. (Wie der Compiler Fehler meldet und wie man sie interpretiert, war Gegenstand der gestrigen Lektion.)
Nachdem diese Lektion alle Teile eines Programms erläutert hat, sollten Sie jedes beliebige Programm ansehen und Ähnlichkeiten feststellen können. Versuchen Sie, die verschiedenen Teile in Listing 2.2 zu erkennen.
Listing 2.2: Das Programm auflisten.c listet Codelistings auf
1: /* auflisten.c Zeigt ein Listing mit Zeilennummern an */
2: #include <stdio.h>
3: #include <stdlib.h>
4:
5: void Verwendung_anzeigen(void);
6: int zeile;
7:
8: int main( int argc, char *argv[] )
9: {
10: char puffer[256];
11: FILE *fp;
12:
13: if( argc < 2 )
14: {
15: Verwendung_anzeigen();
16: return 1;
17: }
18:
19: if (( fp = fopen( argv[1], "r" )) == NULL )
20: {
21: fprintf( stderr, "Fehler beim Öffnen der Datei, %s!", argv[1] );
22: return 1;
23: }
24:
25: zeile = 1;
26:
27: while( fgets( puffer, 256, fp ) != NULL )
28: fprintf( stdout, "%4d:\t%s", zeile++, puffer );
29:
30: fclose(fp);
31: return 0;
32: }
33:
34: void Verwendung_anzeigen(void)
35: {
36: fprintf(stderr, "\nProgramm wie folgt starten: " );
37: fprintf(stderr, "\n\nauflisten Dateiname.ext\n" );
38: }
./auflisten auflisten.c
1: /* auflisten.c Zeigt ein Listing mit Zeilennummern an */
2: #include <stdio.h>
3: #include <stdlib.h>
4:
5: void Verwendung_anzeigen(void);
6: int zeile;
7:
8: int main( int argc, char *argv[] )
9: {
10: char puffer[256];
11: FILE *fp;
12:
13: if( argc < 2 )
14: {
15: Verwendung_anzeigen();
16: return;
17: }
18:
19: if (( fp = fopen( argv[1], "r" )) == NULL )
20: {
21: fprintf( stderr, "Fehler beim Öffnen der Datei, %s!", argv[1] );
22: return;
23: }
24:
25: zeile = 1;
26:
27: while( fgets( puffer, 256, fp ) != NULL )
28: fprintf( stdout, "%4d:\t%s", zeile++, buffer );
29:
30: fclose(fp);
31: return 0;
32: }
33:
34: void Verwendung_anzeigen(void)
35: {
36: fprintf(stderr, "\nProgramm wie folgt starten: " );
37: fprintf(stderr, "\n\nauflisten Dateiname.ext\n" );
38: }
Das Programm auflisten.c
in Listing 2.2 zeigt C-Programmlistings an, die Sie
gespeichert haben. Die Listings werden mit Zeilennummern auf dem Bildschirm
ausgegeben.
Sicherlich sind Sie in der Lage, die verschiedenen Programmteile in Listing 2.2
wiederzuerkennen. Die obligatorische Funktion main()
steht in den Zeilen 8 bis 32. Die
Zeilen 2 und 3 enthalten #include
-Direktiven. In den Zeilen 6, 10 und 11 finden Sie
Variablendefinitionen. Zeile 5 enthält den Funktionsprototyp void
Verwendung_anzeigen(void)
. Weiterhin gehören mehrere Anweisungen in den Zeilen
13, 15, 16, 19, 21, 22, 25, 27, 28, 30, 31, 36 und 37 zum Programm. Die
Funktionsdefinition für Verwendung_anzeigen()
erstreckt sich über die Zeilen 34 bis 38.
Das gesamte Programm hindurch sind Blöcke in geschweiften Klammern
eingeschlossen. Schließlich ist in Zeile 1 ein Kommentar angegeben. In den meisten
Programmen sind wahrscheinlich weit mehr Kommentare angebracht.
Das Programm auflisten
ruft mehrere Funktionen auf. Es enthält nur eine
benutzerdefinierte Funktion - Verwendung_anzeigen()
. Die Funktionen fopen()
in
Zeile 19, fprintf()
in den Zeilen 21, 28, 36 und 37, fgets()
in Zeile 27 und
fclose()
in Zeile 30 sind Bibliotheksfunktionen. Auf diese Bibliotheksfunktionen
gehen die übrigen Lektionen näher ein.
In Listing 2.1 wurden, wie Sie sehen konnten, in Zeile 4 drei Variablen definiert. Computerprogramme arbeiten normalerweise mit unterschiedlichen Datentypen und benötigen eine Möglichkeit, die verwendeten Werte zu speichern. Bei diesen Werten kann es sich um Zahlen oder Zeichen handeln. In C gibt es zwei Möglichkeiten, Zahlenwerte zu speichern - Variablen und Konstanten. Und beide Möglichkeiten verfügen über eine Vielzahl von Optionen. Eine Variable ist eine Speicherstelle mit einem Wert, der sich im Laufe der Programmausführung ändern kann. Eine Konstante hingegen hat einen festen Wert, der nicht geändert wird. Bevor wir jedoch zu den Variablen kommen, sollten Sie erst ein wenig über die Funktionsweise des Speichers in Ihrem Computer erfahren.
Wenn Sie bereits wissen, wie der Speicher eines Computers funktioniert, können Sie diesen Abschnitt überspringen. Wenn Sie sich unsicher sind, lesen Sie einfach weiter. Die hier vermittelten Kenntnisse helfen Ihnen, bestimmte Aspekte der C- Programmierung besser zu verstehen.
Ein Computer legt Informationen in einem Speicher mit wahlfreiem Zugriff (RAM, Random Access Memory) ab. Der RAM - oder Hauptspeicher - ist in Form so genannter Chips realisiert. Der Inhalt dieser Chips ist flüchtig, d.h. die Informationen werden je nach Bedarf gelöscht und durch neue ersetzt. Es bedeutet aber auch, dass sich der RAM nur so lange an diese Informationen »erinnert«, solange der Computer läuft. Schaltet man den Computer aus, gehen auch die gespeicherten Informationen verloren.
In jeden Computer ist RAM eingebaut. Den Umfang des installierten Speichers gibt man in Megabyte (Mbyte) an, wie zum Beispiel 1 Mbyte, 8 Mbyte, 32 Mbyte, 64 Mbyte oder mehr. Ein Megabyte sind 1024 Kilobyte (Kbyte), und ein Kilobyte umfasst 1024 Byte. Ein System mit 4 Mbyte RAM hat also tatsächlich eine Größe von 4 * 1024 Kilobyte bzw. 4096 Kbyte. Das sind 4096 * 1024 Byte oder 4 194 304 Byte RAM.
Ein Byte ist die grundlegende Speichereinheit eines Computers. Näheres über Bytes erfahren Sie in Lektion 18, »Vom Umgang mit dem Speicher«. Tabelle 2.1 gibt einen Überblick, wie viele Byte für die Speicherung bestimmter Arten von Daten erforderlich sind.
Der Hauptspeicher ist fortlaufend organisiert, ein Byte folgt auf ein anderes. Jedes Byte im Speicher lässt sich durch eine eindeutige Adresse ansprechen - eine Adresse, die ein Byte auch von jedem anderen Byte unterscheidet. Die Adressen sind den Speicherstellen in fortlaufender Reihenfolge, beginnend bei 0 und wachsend bis zur maximalen Größe des Systems, zugeordnet. Momentan brauchen Sie sich noch keine Gedanken über Adressen zu machen, der C-Compiler kümmert sich für Sie darum.
Der RAM im Computer wird für mehrere Zwecke verwendet. Als Programmierer haben Sie es aber in erster Linie mit der Datenspeicherung zu tun. Daten sind die Informationen, mit denen ein C-Programm arbeitet. Ob ein Programm eine Adressenliste verwaltet, den Börsenmarkt überwacht, einen Haushaltsplan führt oder die Preise von Schweinefleisch verfolgt - die Informationen (Namen, Aktienkurse, Ausgaben oder zukünftige Preise für Schweinefleisch) werden im RAM gehalten, während das Programm läuft.
Nach diesem kurzen Ausflug in die Hardwarewelt des Computerspeichers geht es wieder zurück zur C-Programmierung und der Art und Weise, wie C im Hauptspeicher Informationen aufbewahrt.
Eine Variable ist eine benannte Speicherstelle für Daten im Hauptspeicher des Computers. Wenn man den Variablennamen in einem Programm verwendet, bezieht man sich damit auf die Daten, die unter diesem Namen abgelegt sind.
Um Variablen in C-Programmen zu verwenden, muss man wissen, wie Variablennamen zu erzeugen sind. In C müssen Variablennamen den folgenden Regeln genügen:
_
) enthalten.
zahl
und Zahl
bezeichnen zwei vollkommen verschiedene Variablen.
Hier einige Beispiele für zulässige und nicht zulässige C-Variablennamen:
Da C die Groß-/Kleinschreibung von Namen beachtet, sind prozent
, PROZENT
und
Prozent
drei unterschiedliche Variablennamen. C-Programmierer verwenden oftmals
nur Kleinbuchstaben in Variablennamen, obwohl dies keineswegs vorgeschrieben ist.
Die durchgängige Großschreibung wird dagegen üblicherweise für Konstanten (siehe
weiter hinten in dieser Lektion) verwendet.
Bei vielen Compilern kann ein Variablenname bis zu 31 Zeichen lang sein.
(Tatsächlich kann er sogar länger sein, der Compiler betrachtet aber nur die ersten 31
Zeichen des Namens.) Damit lassen sich Namen erzeugen, die etwas über die
gespeicherten Daten aussagen. Wenn zum Beispiel ein Programm
Darlehenszahlungen berechnet, könnte es den Wert der ersten Zinsrate in einer
Variablen namens zins_rate
speichern. Aus dem Variablennamen geht die
Verwendung klar hervor. Man hätte auch eine Variable namens x
oder sogar
uwe_seeler
erzeugen können, für den Compiler spielt das keine Rolle. Falls sich aber
ein anderer Programmierer Ihren Quelltext ansieht, bleibt ihm die Bedeutung
derartiger Variablen völlig im Dunkeln. Auch wenn es etwas mehr Aufwand bedeutet,
aussagekräftige Variablennamen einzutippen, der besser verständliche Quelltext ist
diese Mühe allemal wert.
Es gibt zahlreiche Namenskonventionen für Variablennamen, die sich aus mehreren
Wörtern zusammensetzen. Ein Beispiel haben Sie schon gesehen: zins_rate
. Wenn
man die Wörter durch einen Unterstrich voneinander absetzt, lässt sich der
Variablenname leicht interpretieren. Der zweite Stil heißt Kamelnotation. Anstelle
von Leerzeichen (die der Unterstrich verkörpern soll) schreibt man den ersten
Buchstaben jedes Wortes groß und alle Wörter zusammen. Obige Variable trüge dann
den Namen ZinsRate
. Die Kamelnotation gewinnt immer mehr Anhänger, weil sich
ein Großbuchstabe leichter eingeben lässt als der Unterstrich. Das Buch verwendet
allerdings Variablennamen mit Unterstrichen, da derartige Namen besser zu erkennen
sind. Entscheiden Sie selbst, welchem Stil Sie sich anschließen oder ob Sie einen
eigenen entwickeln wollen.
C bietet mehrere Datentypen für numerische Variablen. Diese unterschiedlichen
Variablentypen sind erforderlich, da zum einen die verschiedenartigen numerischen
Werte einen unterschiedlichen Speicherbedarf haben und zum anderen die
ausführbaren mathematischen Operationen nicht für alle Typen gleich sind. Kleine
Ganzzahlen (zum Beispiel 1
, 199
und -8
) erfordern weniger Speicher und der
Computer kann mathematische Operationen mit derartigen Zahlen sehr schnell
ausführen. Im Gegensatz dazu erfordern große Ganzzahlen und Fließkommazahlen
(beispielsweise 123000000
, 3.14
und 0.000000000871256
) mehr Speicherplatz und auch
wesentlich mehr Zeit bei mathematischen Operationen. Wenn man die jeweils
passenden Variablentypen wählt, kann man ein Programm effizienter machen.
Die numerischen C-Variablen lassen sich in zwei Kategorien einteilen:
Innerhalb dieser Kategorien gibt es zwei oder mehrere Variablentypen.
Mit dem in Listing 2.3 vorgestellten Programm können Sie die Größe der Variablen für Ihren Computer ermitteln. Es gibt außerdem die Maximum- und Minimumwerte der Integer-Variablentypen an. Warum sollte man diese Art von Informationen mit einem Programm ermitteln? Wie bereits am Tag 1 kurz angesprochen wurde, läuft Linux auf vielen Rechnern. Dabei ist die Größe einiger Variablentypen auf einem Linux-Rechner mit einem Intel-Pentium-Prozessor (Teil der Intel-IA32-Familie) nicht die gleiche wie auf einem Linux-Rechner, der mit einem DEC-/Compaq-Alpha-Prozessor läuft.
Listing 2.3: groessevon.c - Ein Programm, das die Größe einiger Variablentypen auf Ihrem Computer in Byte anzeigt.
1 : /* Ein Programm, das die Grösse der unterschiedlichen */
2 : /* C-Variablen auf Ihrem Rechner ausgibt. */
3 : #include <stdio.h>
4 : #include <limits.h>
5 :
6 : int main(void)
7 : {
8 : printf ("Signed : Groesse %20s %22s\n", "Min", "Max") ;
9 : printf ("char : %d %22d %22d\n",
10: (int) sizeof (char), CHAR_MIN,CHAR_MAX);
11: printf ("short : %d %22d %22d\n",
12: (int) sizeof (short), SHRT_MIN,SHRT_MAX);
13: printf ("int : %d %22d %22d\n",
14: (int) sizeof (int), INT_MIN,INT_MAX);
15: printf ("long : %d %22ld %22ld\n",
16: (int) sizeof (long), LONG_MIN,LONG_MAX);
17: printf ("\n") ;
18:
19: printf ("Unsigned : Groesse %20s %22s\n", "Min", "Max") ;
20: printf ("char : %d %22d %22u\n",
21: (int) sizeof (unsigned char),0,UCHAR_MAX);
22: printf ("short : %d %22d %22u\n",
23: (int) sizeof (unsigned short),0,USHRT_MAX);
24: printf ("int : %d %22d %22u\n",
25: (int) sizeof (unsigned int),0,UINT_MAX);
26: printf ("long : %d %22d %22lu\n",
27: (int) sizeof (unsigned long),0,ULONG_MAX);
28: printf ("\n") ;
29:
30: printf ("single prec. float : %d\n", (int) sizeof (float));
31: printf ("double prec. float : %d\n", (int) sizeof (double));
32:
33: return 0 ;
34: }
Es soll Sie nicht bekümmern, dass Sie die Funktionsweise des Programms nicht
verstehen. Auch wenn einige Elemente wie zum Beispiel sizeof
neu sind, sind Ihnen
andere mit Sicherheit bekannt. Die Zeilen 1 und 2 sind Kommentare, die den Namen
des Programms und eine kurze Beschreibung enthalten. Die Zeilen 3 und 4 binden
zwei Header-Dateien ein, die standardmäßig von allen ANSI/ISO-C-Compilern
verstanden werden. In diesem einfachen Beispielprogramm gibt es nur eine einzige
Funktion, nämlich main()
in den Zeilen 6 bis 34. Die Zeilen 9 bis 31 bilden den Kern
des Programms. Jede dieser Zeilen gibt eine verbale Beschreibung mit der Größe
jedes Variablentyps aus, wobei das Programm die Größe der Variablen mit dem
Operator sizeof
ermittelt. In Kapitel 17, »Die Bibliothek der C-Funktionen«, erfahren
Sie Näheres zu diesem Operator. Zeile 33 gibt den Wert 0 an das Betriebssystem
zurück, bevor das Programm endet.
Und so sieht die Ausgabe des Programms groessevon.c
aus, das auf einem Linux-
Rechner mit einem Intel-x86-Prozessor kompiliert und ausgeführt wurde.
./groessevon
Signed : Groesse Min Max
char : 1 -128 127
short : 2 -32768 32767
int : 4 -2147483648 2147483647
long : 4 -2147483648 2147483647
Unsigned : Groesse Min Max
char : 1 0 255
short : 2 0 65535
int : 4 0 4294967295
long : 4 0 4294967295
single prec. float : 4
double prec. float : 8
Und so sieht die Ausgabe des Programms groessevon.c
aus, das auf einem Linux-
Rechner mit einem DEC/Compaq Alpha-Prozessor kompiliert und ausgeführt wurde.
./groessevon
Signed : Groesse Min Max
char : 1 -128 127
short : 2 -32768 32767
int : 4 -2147483648 2147483647
long : 8 -9223372036854775808 9223372036854775807
Unsigned : Groesse Min Max
char : 1 0 255
short : 2 0 65535
int : 4 0 4294967295
long : 8 0 18446744073709551615
single prec. float : 4
double prec. float : 8
Beachten Sie, dass Integer-Variablen standardmäßig vorzeichenbehaftet (signed
) sind,
das heißt, es bedarf keines besonderen Schlüsselwortes, um Integer-Variablen mit
Vorzeichen zu versehen. Auffallen sollte Ihnen auch, dass die Ergebnisse beider
Prozessoren für char
, short
und int
identisch sind, aber für die größeren Datentypen
stark abweichen. Dies liegt daran, dass der Alpha-Prozessor ein 64-Bit-Prozessor ist
und Integer-Werte bis 2^64-1 darstellen kann, während Prozessoren der Intel-
Pentium-Familie 32-Bit-Prozessoren sind und nur Zahlen bis 2^32-1 darstellen
können. Programmierer, deren Code zwischen Alpha- und Pentium-Prozessoren
portierbar sein soll, sollten sich diesen Unterschied merken.
Auch wenn sich die Größe von Datentypen je nach Computerplattform unterscheiden kann, gibt C Dank des ANSI-Standards einige Garantien. Auf die folgenden fünf Dinge können Sie sich verlassen:
char
beträgt ein Byte.
short
ist kleiner oder gleich der Größe eines int
.
int
ist kleiner oder gleich der Größe eines long
.
unsigned int
ist gleich der Größe eines int
.
float
ist kleiner oder gleich der Größe eines double
.
Die Werte in Fließkomma-Variablen entsprechen der wissenschaftlichen Notation, die Ihnen noch von der Schule her bekannt sein dürfte. Fließkommazahlen bestehen aus drei Teilen: einem Vorzeichen (+ oder - für die Darstellung positiver und negativer Zahlen), einer Mantisse (ein Wert zwischen 0 und 1) und einem Exponenten. Wenn man diese Notation zugrunde legt, würde eine Zahl wie 23.85 als +0.2385E2 dargestellt: eine positive Zahl gleich 0.2385 mal 10 hoch 2 (das heißt mal 100). Der Exponent kann sowohl positiv als auch negativ sein, so dass damit sehr große sowie sehr kleine Zahlen dargestellt werden können.
Diese Darstellung von Fließkommazahlen ist jedoch nicht für alle Zahlen absolut genau. Viele Zahlen, wie zum Beispiel 1/3 können mit Fließkommazahlen nur annähernd beschrieben werden. 1/3 wird korrekt als 0.333333..... mit einer unendlichen Folge von Dreien wiedergegeben. Fließkommazahlen können jedoch nur eine begrenzte Anzahl dieser sich wiederholenden Dreien abspeichern. Bei einer Darstellung von 1/3 mit einfacher Genauigkeit (single precision) werden sieben dieser Dreien und bei einer Darstellung mit doppelter Genauigkeit (double precision) 19 dieser Dreien abgespeichert.
Die Darstellungen der Fließkommazahlen in einfacher und doppelter Genauigkeit unterscheiden sich auch in dem Wertebereich, den sie speichern können. Variablen doppelter Genauigkeit können wesentlich größer und wesentlich kleiner sein als Variablen einfacher Genauigkeit. Der Grund dafür liegt darin, dass der Exponent der Darstellung mit doppelter Genauigkeit einen größeren Zahlenbereich umfasst als der Exponent bei einfacher Genauigkeit. In Tabelle 2.2 werden der Wertebereich und die Genauigkeit der Fließkommazahlen mit doppelter und einfacher Genauigkeit miteinander verglichen.
Bevor man eine Variable in einem C-Programm verwenden kann, muss man sie deklarieren. Eine Variablendeklaration teilt dem Compiler den Namen und den Typ der Variablen mit. Die Deklaration kann die Variable auch mit einem bestimmten Wert initialisieren. Wenn ein Programm versucht, eine vorher nicht deklarierte Variable zu verwenden, liefert der Compiler eine Fehlermeldung. Eine Variablendeklaration hat die folgende Form:
typbezeichner variablenname;
Der typbezeichner gibt den Variablentyp an und muss einem der in Listing 2.3 verwendeten Schlüsselwörter entsprechen. Der variablenname gibt den Namen der Variablen an und muss den weiter vorn angegebenen Regeln genügen. In ein und derselben Zeile kann man mehrere Variablen desselben Typs deklarieren, wobei die einzelnen Variablennamen durch Kommata zu trennen sind:
int zaehler, zahl, start; /* Drei Integer-Variablen */
float prozent, gesamt; /* Zwei Fließkommavariablen */
Wie Tag 11, »Gültigkeitsbereiche von Variablen«, zeigen wird, ist der Ort der
Variablendeklaration im Quellcode wichtig, weil er die Art und Weise beeinflusst, in
der ein Programm die Variablen verwenden kann. Fürs Erste können Sie aber alle
Variablendeklarationen zusammen unmittelbar vor der Funktion main()
angeben.
Mit dem Schlüsselwort typedef
lässt sich ein neuer Name für einen vorhandenen
Datentyp erzeugen. Im Grunde erzeugt typedef
ein Synonym. Beispielsweise definiert
die Anweisung
typedef int integer;
den Bezeichner integer
als Synonym für int
. Von nun an können Sie Variablen vom
Typ int
mit dem Synonym integer
wie im folgenden Beispiel definieren:
integer zaehler;
Beachten Sie, dass typedef
keinen neuen Datentyp erstellt, sondern lediglich die
Verwendung eines anderen Namens für einen vordefinierten Datentyp erlaubt. Das
Schlüsselwort typedef
verwendet man vor allem in Verbindung mit
zusammengesetzten Datentypen, wie am Tag 10 zum Thema Strukturen erläutert. Ein
zusammengesetzter Datentyp besteht aus einer Kombination der in der heutigen
Lektion vorgestellten Datentypen.
Wenn man eine Variable deklariert, weist man den Compiler an, einen bestimmten Speicherbereich für die Variable zu reservieren. Allerdings legt man dabei nicht fest, welcher Wert - d.h. der Wert der Variablen - in diesem Bereich zu speichern ist. Dies kann der Wert 0 sein, aber auch irgendein zufälliger Wert. Bevor Sie eine Variable verwenden, sollten Sie ihr immer einen bekannten Anfangswert zuweisen. Dies können Sie unabhängig von der Variablendeklaration mit einer Zuweisung wie im folgenden Beispiel erreichen:
int zaehler; /* Speicherbereich für die Variable zaehler reservieren */
zaehler = 0; /* Den Wert 0 in der Variablen zaehler speichern */
Das Gleichheitszeichen in dieser Anweisung ist der Zuweisungsoperator der Sprache C. Auf diesen und andere Operatoren gehe ich am Tag 3, »Anweisungen, Ausdrücke und Operatoren«, näher ein. Hier sei lediglich erwähnt, dass das Gleichheitszeichen in der Programmierung nicht die gleiche Bedeutung hat wie in der Mathematik. Wenn man zum Beispiel
x = 12
als algebraischen Ausdruck betrachtet, bedeutet das: »x ist gleich 12«. In C dagegen drückt das Gleichheitszeichen den folgenden Sachverhalt aus: »Weise den Wert 12 der Variablen x zu.«
Variablen kann man auch im Zuge der Deklaration initialisieren. Dazu schreibt man in der Deklarationsanweisung nach dem Variablennamen ein Gleichheitszeichen und den gewünschten Anfangswert:
int zaehler = 0;
double prozent = 0.01, steuersatz = 28.5;
Achten Sie darauf, eine Variable nicht mit einem Wert außerhalb des zulässigen Bereichs zu initialisieren. Zum Beispiel sind folgende Initialisierungen fehlerhaft:
int gewicht = 10000000000000;
unsigned int wert = -2500;
Zum Glück gibt der GNU-C-Compiler eine Warnung aus, wenn er einen solchen Code kompilieren soll. Es ist jedoch nur eine Warnung. Sie können das Programm trotzdem kompilieren und linken, erhalten aber unerwartete Ergebnisse, wenn das Programm läuft.
Wie eine Variable ist auch eine Konstante ein Speicherbereich für Daten, mit dem ein Programm arbeiten kann. Im Gegensatz zu einer Variablen lässt sich der in einer Konstanten gespeicherte Wert während der Programmausführung nicht ändern. C kennt zwei Arten von Konstanten für unterschiedliche Einsatzgebiete:
Eine literale Konstante ist ein Wert, den man direkt im Quellcode angibt. D.h. man schreibt den Wert an allen Stellen, wo er vorkommt, »wörtlich« (literal) aus:
int zaehler = 20;
float steuer_satz = 0.28;
Die Zahlen 20
und 0.28
sind literale Konstanten. Die obigen Anweisungen speichern
diese Werte in den Variablen zaehler
und steuer_satz
. Eine der beiden Konstanten
enthält einen Dezimalpunkt, die andere nicht. Ein vorhandener bzw. nicht
vorhandener Dezimalpunkt unterscheidet Fließkommakonstanten von Integer-
Konstanten.
In C sind Fließkommazahlen mit einem Punkt zu schreiben, d.h. nicht mit einem Komma, wie es in deutschsprachigen Ländern üblich ist.
Enthält eine literale Konstante einen Dezimalpunkt, gilt sie als Fließkommakonstante,
die der C-Compiler als eine Zahl vom Typ double
auffasst. Fließkommakonstanten
lassen sich in der gewohnten Dezimalschreibweise wie in den folgenden Beispielen
schreiben:
123.456
0.019
100.
Beachten Sie, dass in der dritten Konstanten nach der Zahl 100
ein Dezimalpunkt
steht, auch wenn es sich um eine ganze Zahl handelt (d.h. eine Zahl ohne
gebrochenen Anteil). Der Dezimalpunkt bewirkt, dass der C-Compiler die Konstante
wie eine Fließkommazahl doppelter Genauigkeit behandelt. Ohne den Dezimalpunkt
nimmt der Compiler eine Integer-Konstante an.
Fließkommakonstanten können Sie auch in wissenschaftlicher Notation angeben.
Vielleicht erinnern Sie sich noch aus Schultagen, dass die wissenschaftliche Notation
eine Zahl als Dezimalteil mal 10 hoch einer positiven oder negativen Zahl darstellt.
Diese Notation bietet sich vor allem für sehr große und sehr kleine Zahlen an. In C
schreibt man Zahlen in wissenschaftlicher Notation als Dezimalzahl mit einem
nachfolgenden E
oder e
und dem Exponenten:
Eine Konstante ohne Dezimalpunkt fasst der Compiler als Integer-Zahl auf. Integer- Zahlen kann man in drei verschiedenen Notationen schreiben:
Eine symbolische Konstante ist eine Konstante, die durch einen Namen (Symbol) im Programm dargestellt wird. Wie literale Konstanten kann sich auch der Wert von symbolischen Konstanten nicht ändern. Wenn Sie in einem Programm auf den Wert einer symbolischen Konstanten zugreifen wollen, verwenden Sie den Namen dieser Konstanten genau wie bei einer Variablen. Den eigentlichen Wert der symbolischen Konstanten muss man nur einmal eingeben, wenn man die Konstante definiert.
Symbolische Konstanten haben gegenüber literalen Konstanten zwei wesentliche
Vorteile, wie es die folgenden Beispiele verdeutlichen. Nehmen wir an, dass Sie in
einem Programm eine Vielzahl von geometrischen Berechnungen durchführen. Dafür
benötigt das Programm häufig den Wert für die Kreiszahl (~3.14). Um zum Beispiel
den Umfang und die Fläche eines Kreises bei gegebenem Radius zu berechnen,
schreibt man:
umfang = 3.14 * (2 * radius);
flaeche = 3.14 * (radius) * (radius);
Das Sternchen (*
) stellt den Multiplikationsoperator von C dar. (Operatoren sind
Gegenstand von Tag 3.) Die erste Anweisung bedeutet: »Multipliziere den in der
Variablen radius
gespeicherten Wert mit 2
und multipliziere dieses Ergebnis mit 3.14
.
Weise dann das Ergebnis an die Variable umfang
zu.«
Wenn Sie allerdings eine symbolische Konstante mit dem Namen PI
und dem Wert
3.14
definieren, können Sie die obigen Anweisungen wie folgt formulieren:
umfang = PI * (2 * radius);
flaeche = PI * (radius) * (radius);
Der Code lässt sich dadurch besser verstehen. Statt darüber zu grübeln, ob mit 3.14
tatsächlich die Kreiszahl gemeint ist, erkennt man diese Tatsache unmittelbar aus dem
Namen der symbolischen Konstanten.
Der zweite Vorteil von symbolischen Konstanten zeigt sich, wenn man eine Konstante
ändern muss. Angenommen, Sie wollen in den obigen Beispielen mit einer größeren
Genauigkeit rechnen. Dazu geben Sie den Wert PI
mit mehr Dezimalstellen an:
3.14159
statt 3.14
. Wenn Sie literale Konstanten im Quelltext geschrieben haben,
müssen Sie den gesamten Quelltext durchsuchen und jedes Vorkommen des Wertes
3.14
in 3.14159
ändern. Mit einer symbolischen Konstanten ist diese Änderung nur
ein einziges Mal erforderlich, und zwar in der Definition der Konstanten.
In C lassen sich symbolische Konstanten nach zwei Verfahren definieren: mit der
Direktive #define
und mit dem Schlüsselwort const
. Die #define
-Direktive verwendet
man wie folgt:
#define KONSTANTENNAME Wert
Damit erzeugt man eine Konstante mit dem Namen KONSTANTENNAME
und einem Wert,
der in Wert
als literale Konstante angegeben ist. Der Bezeichner KONSTANTENNAME
folgt
den gleichen Regeln wie sie weiter vorn für Variablennamen genannt wurden. Per
Konvention schreibt man Namen von Konstanten durchgängig in Großbuchstaben.
Damit lassen sie sich leicht von Variablen unterscheiden, deren Namen man per
Konvention in Kleinbuchstaben oder in gemischter Schreibweise schreibt. Für das
obige Beispiel sieht die #define
-Direktive für eine Konstante PI
wie folgt aus:
#define PI 3.14159
Beachten Sie, dass Zeilen mit #define
-Direktiven nicht mit einem Semikolon enden.
Man kann zwar #define
-Direktiven an beliebigen Stellen im Quellcode angeben,
allerdings wirken sie nur auf die Teile des Quellcodes, die unter der #define
-Direktive
stehen. In der Regel gruppiert man alle #define
-Direktiven an einer zentralen Stelle
am Beginn der Datei und vor dem Start der Funktion main()
.
Eine #define
-Direktive teilt dem Compiler Folgendes mit: »Ersetze im Quellcode die
Zeichenfolge KONSTANTENNAME
durch wert
.« Die Wirkung ist genau die gleiche, als wenn
man mit dem Editor den Quellcode durchsucht und jede Ersetzung manuell vornimmt.
Beachten Sie, dass #define
keine Zeichenfolge ersetzt, wenn diese Bestandteil eines
längeren Namens, Teil eines Kommentars oder in Anführungszeichen eingeschlossen
ist. Zum Beispiel wird das Vorkommen von PI
in der zweiten und dritten Zeile nicht
ersetzt:
#define PI 3.14159
/* Sie haben eine Konstante für PI definiert. */
#define PIPETTE 100
Die
#define
-Direktive gehört zu den Präprozessoranweisungen von C, auf die am Tag 20, »Compiler für Fortgeschrittene«, umfassend eingegangen wird.
Eine symbolische Konstante kann man auch mit dem Schlüsselwort const
definieren.
Das Schlüsselwort const
ist ein Modifizierer, der sich auf jede Variablendeklaration
anwenden lässt. Eine als const
deklarierte Variable lässt sich während der
Programmausführung nicht modifizieren, sondern nur zum Zeitpunkt der Deklaration
initialisieren. Dazu einige Beispiele:
const int zaehler = 100;
const float pi = 3.14159;
const long schulden = 12000000, float steuer_satz = 0.21;
Das Schlüsselwort const
bezieht sich auf alle Variablen der Deklarationszeile. In der
letzten Zeile sind schulden
und steuer_satz
symbolische Konstanten. Wenn ein
Programm versucht, eine als const
deklarierte Variable zu verändern, erzeugt der
Compiler eine Fehlermeldung, wie es beispielsweise bei folgendem Code der Fall ist:
const int zaehler = 100;
zaehler = 200; /* Wird nicht kompiliert! Der Wert von Konstanten kann */
/* weder neu zugewiesen noch geändert werden. */
Welche praktischen Unterschiede bestehen zwischen symbolischen Konstanten, die
man mit der #define
-Direktive erzeugt, und denjenigen mit dem Schlüsselwort const
?
Das Ganze hat mit Zeigern und dem Gültigkeitsbereich von Variablen zu tun. Hierbei
handelt es sich um zwei sehr wichtige Aspekte der C-Programmierung, auf die wir an
den Tagen 8, »Zeiger«, und 11, »Gültigkeitsbereiche von Variablen«, näher eingehen
werden.
Sehen Sie sich jetzt ein Programm an, das demonstriert, wie man Variablen deklariert und literale und symbolische Konstanten verwendet. Das in Listing 2.4 wiedergegebene Programm fragt den Benutzer nach seinem Gewicht und Geburtsjahr. Dann rechnet es das Gewicht in Gramm um und berechnet das Alter für das Jahr 2010. Das Programm können Sie entsprechend den in Kapitel 1 vorgestellten Schritten eingeben, kompilieren und ausführen.
Listing 2.4: Ein Programm, das zeigt, wie man Variablen und Konstanten verwendet.
1: /* Demonstriert die Verwendung von Variablen und Konstanten */
2: #include <stdio.h>
3:
4: /* Konstante zur Umrechnung von Pfund in Gramm definieren */
5: #define GRAMM_PRO_PFUND 500
6:
7: /* Konstante für Beginn des nächsten Jahrzehnts definieren */
8: const int ZIEL_JAHR = 2010;
9:
10: /* Erforderliche Variablen deklarieren */
11: int gewicht_in_gramm, gewicht_in_pfund;
12 int jahr_der_geburt, alter_in_2010;
13:
14: int main()
15: {
16: /* Daten vom Benutzer einlesen */
17:
18: printf("Bitte Ihr Gewicht in Pfund eingeben: ");
19: scanf("%d", &gewicht_in_pfund);
20: printf("Bitte Ihr Geburtsjahr eingeben: ");
21: scanf("%d", &jahr_der_geburt);
22:
23: /* Umrechnungen durchführen */
24:
25: gewicht_in_gramm = gewicht_in_pfund * GRAMM_PRO_PFUND;
26: alter_in_2010 = ZIEL_JAHR - jahr_der_geburt;
27:
28: /* Ergebnisse auf Bildschirm ausgeben */
29:
30: printf("\nIhr Gewicht in Gramm = %d", gewicht_in_gramm);
31: printf("\nIm Jahr 2010 sind Sie %d Jahre alt.\n", alter_in_2010);
32:
33: return 0;
34: }
Bitte Ihr Gewicht in Pfund eingeben: 175
Bitte Ihr Geburtsjahr eingeben: 1960
Ihr Gewicht in Gramm = 87500
Im Jahr 2010 sind Sie 50 Jahre alt.
Das Programm deklariert in den Zeilen 5 und 8 zwei Arten von symbolischen
Konstanten. Die in Zeile 5 deklarierte Konstante dient dazu, die Umrechnung von
Pfund in Gramm (das heißt den Wert 500) verständlicher zu formulieren, wie es in Zeile
25 geschieht. Die Zeilen 11 und 12 deklarieren Variablen, die in anderen Teilen des
Programms zum Einsatz kommen. Aus den beschreibenden Namen wie
gewicht_in_gramm
lässt sich die Bedeutung einer Berechnung leichter nachvollziehen.
Die Zeilen 18 und 20 geben die Texte für die Eingabeaufforderungen auf den
Bildschirm aus. Die Funktion printf()
wird später in diesem Buch noch im Detail
behandelt. Damit der Benutzer auf die Aufforderungen reagieren kann, verwenden die
Zeilen 19 und 21 eine weitere Bibliotheksfunktion, scanf()
, mit der sich Eingaben über
die Tastatur entgegennehmen lassen. Auch auf diese Funktion geht das Buch später
näher ein. Die Zeilen 25 und 26 berechnen das Gewicht des Benutzers in Gramm und
sein Alter für das Jahr 2010. Diese und andere Anweisungen kommen in der morgigen
Lektion zur Sprache. Am Ende des Programms zeigen die Zeilen 30 und 31 die
Ergebnisse für den Benutzer an.
Verwenden Sie Konstanten, um Ihre Programme verständlicher zu machen. | Versuchen Sie nicht, einer Konstanten einen Wert zuzuweisen, nachdem diese bereits initialisiert wurde. |
Diese Lektion hat Ihnen die Hauptkomponenten eines typischen C-Programms
vorgestellt. Ausführlich haben wir uns mit den numerischen Variablen beschäftigt,
die in C-Programmen dazu dienen, während der Programmausführung Daten zu
speichern. Sie haben gelernt, dass der einzig erforderliche Teil jedes C-Programms die
Funktion main()
ist. Die eigentliche Arbeit erledigen die Programmanweisungen, die
den Computer instruieren, die gewünschten Aktionen auszuführen. Weiterhin haben
Sie Variablen und Variablendefinitionen kennen gelernt und erfahren, wie man
Kommentare im Quellcode verwendet.
Neben der Funktion main()
kann ein C-Programm zwei Arten von Funktionen
enthalten: Bibliotheksfunktionen, die zum Lieferumfang des Compilers gehören, und
benutzerdefinierte Funktionen, die der Programmierer erstellt.
Sie haben gelernt, dass es zwei Kategorien von numerischen Variablen gibt - Integer
und Fließkomma. Innerhalb dieser Kategorien gibt es verschiedene spezifische
Variablentypen. Welchen Variablentyp - int
, long
, float
oder double
- man für eine
bestimmte Anwendung einsetzt, hängt von der Natur der Daten ab, die in der
Variablen zu speichern sind. Es wurde auch gezeigt, dass man in einem C-Programm
eine Variable zuerst deklarieren muss, bevor man sie verwenden kann. Eine
Variablendefinition informiert den Compiler über den Namen und den Typ der
Variablen.
Ein weiteres Thema dieser Lektion waren Konstanten. Dabei haben Sie die beiden
Konstantentypen von C - literale und symbolische Konstanten - kennen gelernt. Im
Gegensatz zu den Variablen lässt sich der Wert einer Konstanten während der
Programmausführung nicht verändern. Literale Konstanten geben Sie direkt in den
Quelltext ein, wann immer der entsprechende Wert erforderlich ist. Symbolischen
Konstanten ist ein Name zugewiesen, und unter diesem Namen beziehen Sie sich im
Quelltext auf den Wert der Konstanten. Symbolische Konstanten erzeugt man mit der
#define
-Direktive oder mit dem Schlüsselwort const
.
Frage:
Welche Wirkung haben Kommentare auf ein Programm?
Antwort:
Kommentare sind für den Programmierer gedacht. Wenn der Compiler den
Quellcode in Objektcode überführt, ignoriert er Kommentare sowie
Leerzeichen, Tabulatoren etc., die nur der Gliederung des Quelltextes dienen
(so genannter Whitespace). Das bedeutet, dass Kommentare keinen Einfluss
auf das ausführbare Programm haben. Ein Programm mit zahlreichen
Kommentaren läuft genauso schnell wie ein Programm, das überhaupt keine
oder nur wenige Kommentare hat. Kommentare vergrößern zwar die
Quelldatei, was aber gewöhnlich von untergeordneter Bedeutung ist. Fazit:
Verwenden Sie Kommentare und Whitespace, um den Quellcode so
verständlich wie möglich zu gestalten.
Frage:
Worin besteht der Unterschied zwischen einer Anweisung und einem Block?
Antwort:
Ein Block ist eine Gruppe von Anweisungen, die in geschweiften Klammern
({}
) eingeschlossen sind. Einen Block kann man an allen Stellen verwenden,
wo auch eine Anweisung stehen kann.
Frage:
Wie kann ich herausfinden, welche Bibliotheksfunktionen verfügbar sind?
Antwort:
Zum Standardumfang von Linux gehören Hunderte von Bibliotheken. Einige
von ihnen können Sie anzeigen, indem Sie die Verzeichnisse /lib und /usr/lib
auflisten lassen. Am wichtigsten ist die Standard-C-Bibliothek, die /lib/libc-
2.1.1.so oder so ähnlich heißt. Diese Bibliothek enthält eine riesige Auswahl
an vordefinierten Funktionen für Tastatureingabe/Bildschirmausgabe,
Dateiein-/-ausgabe, Stringmanipulation, Mathematik, Speicherallokation und
Fehlerbehandlung. All diese vordefinierten Funktionen sind vollständig in
dem libc-Abschnitt der GNU-Infoseiten dokumentiert und nach ihrer
Funktionalität kategorisiert. Sie können die Dokumentation mit einem der
Informations-Leseprogramme, die bereits am Tag 1 erwähnt wurden,
einsehen. Mit einem der beiden folgenden Befehle können Sie direkt zu dem
libc-Abschnitt springen:
gnome-help-browser info:libc
info libc
Mit kdehelp gibt es keinen direkten Weg, die libc-Dokumentation anzusteuern, aber es ist relativ einfach, dort die GNU-Informationsseiten und den darin enthaltenen libc-Abschnitt zu finden.
Frage:
Was passiert, wenn ich eine Zahl mit gebrochenem Anteil einer Integer-Variablen
zuweise?
Zahlen mit gebrochenem Anteil kann man durchaus einer Variablen vom Typ
int
zuweisen. Wenn Sie eine konstante Variable verwenden, gibt der
Compiler möglicherweise eine Warnung aus. Der zugewiesene Wert wird am
Dezimalpunkt abgeschnitten. Wenn Sie einer Integer-Variablen namens pi
zum Beispiel 3.14
zuweisen, enthält pi
den Wert 3
. Der gebrochene Anteil .14
geht schlicht und einfach verloren.
Frage:
Was passiert, wenn ich eine Zahl einer Variablen zuweise, deren Typ für die Zahl
nicht groß genug ist?
Antwort:
Viele Compiler erlauben solche Zuweisungen, ohne einen Fehler dafür
auszugeben. Die Zahl wird dabei in der Art eines Kilometerzählers angepasst,
d.h. wenn der Maximalwert überschritten ist, beginnt die Zählung wieder von
vorn. Wenn Sie einer vorzeichenbehafteten Integer-Variablen (Typ signed int
)
zum Beispiel 32768
zuweisen, enthält die Variable am Ende den Wert -32768
.
Und wenn Sie dieser Integer-Variablen den Wert 65535
zuweisen, steht
tatsächlich der Wert -1
in der Variablen. Ziehen Sie den Maximalwert, den die
Variable aufnehmen kann, vom zugewiesenen Wert ab. Damit erhalten Sie
den Wert, der tatsächlich gespeichert wird.
Frage:
Was passiert, wenn ich eine negative Zahl in eine vorzeichenlose Variable schreibe?
Antwort:
Wie in der vorherigen Antwort bereits erwähnt, bringt der Compiler
wahrscheinlich keine Fehlermeldung. Er behandelt die Zahl genauso wie bei
der Zuweisung einer zu großen Zahl. Wenn Sie zum Beispiel einer Variablen
vom Typ unsigned int
, die zwei Bytes lang ist, die Zahl -1
zuweisen, nimmt
der Compiler den größtmöglichen Wert, der sich in der Variablen speichern
lässt (in diesem Fall 65535
).
Frage:
Welche praktischen Unterschiede bestehen zwischen symbolischen Konstanten,
die man mit der Direktive #define
erzeugt, und Konstanten, die man mit dem
Schlüsselwort const
deklariert?
Antwort:
Die Unterschiede haben mit Zeigern und dem Gültigkeitsbereich von
Variablen zu tun. Hierbei handelt es sich um zwei sehr wichtige Aspekte der
C-Programmierung, auf die die Tage 8 und 11 eingehen. Fürs Erste sollten
Sie sich merken, dass sich ein Programm leichter verstehen lässt, wenn man
Konstanten mit #define
erzeugt.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, sowie Übungen, die Sie anregen sollen, das Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Die Lösungen zu den Fragen und den Übungen finden Sie in Anhang C.
#include
-Direktive?
1: /* Ueb2-2.c */
2: #include <stdio.h>
3:
4: void anzeigen_zeile(void);
5:
6: int main()
7: {
8: anzeigen_zeile();
9: printf("\n C in 21 Tagen\n");
10: anzeigen_zeile();
11: printf("\n\n");
12: return 0;
13: }
14:
15: /* Zeile mit Sternchen ausgeben */
16: void anzeigen_zeile(void)
17: {
18: int zaehler;
19:
20: for( zaehler = 0; zaehler < 21; zaehler++ )
21: printf("*" );
22: }
23: /* Programmende */
1: /* Ueb2-4.c */
2: #include <stdio.h>
3:
4: int main()
5: {
6: int ctr;
7:
8: for( ctr = 65; ctr < 91; ctr++ )
9: printf("%c", ctr );
10:
11: return 0;
12: }
13: /* Programmende */
1: /* Ueb2-5.c */
2: #include <stdio.h>
3: #include <string.h>
4: int main()
5: {
6: char puffer[256];
7:
8: printf( "Bitte Name eingeben und <Eingabe> druecken:\n");
9: fgets( puffer,256,stdin );
10:
11: printf( "\nIhr Name enthält %d Zeichen (inkl. Leerzeichen).",
12 strlen( puffer ));
13:
14: return 0;
15: }