NorthC


Teil 1

geschrieben vom 16.01.1998 bis 27.01.1998 von Enrico Bauermeister


Vorwort

Da bei der Redaktion der AmigaToday leider keine Wünsche geäußert wurden, was f¸r Workshops gewünscht werden, habe ich mich dazu entschlossen einen Kurs f¸r die Programmiersprache C zu schreiben. Bei Wünsche, Anregungen, Ideen, Probleme oder auch Kritik wendet euch bitte an mich (Adresse und Telefonnummer stehen im Impressum der Amiga Today).


Software

Damit ihr diesen Kurs auch nachvollziehen k–nnt, braucht ihr natürlich auch die entsprechende Software. In diesem Fall einen C-Compiler. Auf dem Amiga existieren diese Compiler wie Sand am Meer. Einige von ihnen werden kommerziell vertrieben, andere als Free- oder Shareware. Wir werden einen Public-Domain-Compiler verwenden, nämlich NorthC. Dieser Compiler ist zwar nicht das Gelbe vom Ei, aber zum Kennenlernen der Sprache C völlig ausreichend. Aber kommen wir erstmal zur Installation des Compilers.


Benutzung von NorthC von Disketten

Wollt ihr NorthC von Disketten benutzen, braucht ihr natürlich auch nichts zu installieren. Ihr müßt lediglich die Scripte "single-disk" (wenn ihr nur ein Diskettenlaufwerk besitzt) oder "dual-disk" (wenn ihr mehr als ein Diskettenlaufwerk besitzt) von Disk "NorthC" starten. Danach ist alles zum Arbeiten vorbereitet.


HardDisk-Installation von NorthC

Legt irgendwo auf eurer HardDisk ein Verzeichnis an und nennt es "NorthC". Nun kopiert von der Diskette "NorthC" die Verzeichnisse "bin", "clibs" und "include" in dieses Verzeichnis. Die anderen Verzeichnisse und Dateien auf der Diskette benötigen wir nicht. Die Beispiel-Quelltexte von der Diskette "NorthC Examples" könnt ihr auch noch kopieren, oder laßt sie einfach auf Diskette. Anschließend müßt ihr noch ein paar Zeilen in eure User-Startup hinzufügen. Hier ein Beispiel falls ihr NorthC direkt auf DH0: installiert habt:

assign clibs: DH0:NorthC/clibs
path DH0:NorthC/bin add

Wenn ihr NorthC woanders installiert habt, müßt ihr selbstverständlich die Zeilen ändern bzw. eure Pfadnamen eintragen.


Include-Path setzen

Damit der Compiler seine Inlucde-Dateien auch findet, müssen wir noch eine Environment-Variable setzen. Gebt im CLI ein:

für Diskettenbetrieb:
setenv INCLUDE NorthC:Include

für Festplattenbetrieb (tragt hier wieder euren Pfadnamen ein):
setenv INCLUDE DH0:NorthC/Include


Unser erstes Programm

Nun ist es an der Zeit unser erstes C-Programm zu schreiben. Wie üblich handelt es sich dabei um das klassische "Hello, World!"-Programm. Schaut euch erstmal den Quelltext f¸r dieses Programm an. Nun wollen wir das Programm übersetzen und ausführen. Um vom Quelltext zum ausführbaren Programm zu kommen sind drei Schritte nötig:

1. Quelltext mit Compiler übersetzen
2. den erzeugten Assemblerquelltext mit einem Assembler übersetzen
3. die erzeugte Objektdatei mit einem Linker zusammen mit der CLib linken

Um nun nicht jedesmal alle drei Schritte einzeln aufrufen zu müssen, wurde das Programm "cc" geschrieben (den UNIX-Freaks unter euch dürfte das wohl bekannt sein). Dann tippt mal im CLI ein:

cc <quelltext.c> -o<programmname>

F¸r <quelltext.c> setzt ihr den Dateinamen eures C-Quelltextes ein, für <programmname> den Dateinamen des zu erzeugenden ausführbaren Programmms. Wenn euer Quelltext keine Fehler enthält und ihr keinen Fehler beim installieren des Compiler gemacht habt, solltet ihr jetzt euer Programm starten können.


Erkl”rung des ersten Programms

Der Text zwischen "/*" und "/*" ist ein Kommentartext, der vom Compiler ignoriert wird. Ihr solltet eure Programme mit vielen Kommentaren versehen, so wird es leichter das Programm nach einer gewissen Zeit wieder zu verstehen (falls ihr später mal was dran ändern möchtet).

Die nächste Anweisung (#include ) ist eine Preprozessor-Anweisung. Nun werdet ihr sicherlich fragen was in aller Welt ist ein Preprozessor. Ein Preprozessor ist ein Programm, daþ euren C-Quelltext vor dem Compiler bearbeitet und euren Quelltext aufbereitet. Zuerst werden erstmal alle Kommentare entfernt und anschließend die Anweisungen für den Preprozessor ausgeführt. Jede Preprozessor-Anweisung beginnt mit dem Zeichen "#". Die Anweisung "include" benötigt als Argument immer eine Datei mit C-Quelltext deren Inhalt an die Stelle im Quelltext gesetzt wird, wo vorher die Anweisung "include" stand. Die Include-Anweisung wird nach Abarbeitung aus dem Quelltext gelöscht. Wird der Dateiname bei "include" in "<" und ">" eingeschlossen, wird immer im Include-Verzeichnis gesucht (im Falle von NorthC das Verzeichnis, daß ihr in der Environment-Variable INCLUDE gesetzt habt). Wird der Name aber in Anführungszeichen ("") eingeschlossen, wird im selben Verzeichnis gesucht, wo sich der Quelltext befindet der die Include-Anweisung enthielt. Die Datei "stdio.h" ist eine Header-Datei, die Deklarationen von Funktionen für die Ein- und Ausgabe enthält. Und genau diese Funktionen (jedenfalls eine) brauchen wir f¸r unser Programm.

Als nächstes kommen wir zu der Zeile "void main()". Dies ist eine Funktion oder Prozedur (für die PASCAL-Leute) in C. Bei der Funktion "main" handelt es sich aber um eine ganz besondere Funktion: es ist die erste die beim Programmablauf aufgerufen wird, und muß daher logischer Weise in jedem C-Programm vorhanden sein. Das Wort "void" beschreibt den Typ des Rückgabewertes der Funktion "main". "void" heißt typenlos, also muß (und darf) hier "main" keinen Wert zurückliefern. Normalerweise sollte "main" immer den Wert 0 zurückliefern, außer bei Fehlern einen Wert ungleich 0. Das leere Klammernpaar hinter "main" bedeutet, daß wir hier keine Parameter an die Funktion übergeben. Der Anfang und das Ende einer Funktion wird durch zwei geschweifte Klammern - "{" und "}" - gekennzeichnet.

Nun kommen wir endlich zu der Funktion die den Text ausgibt, nämlich printf(). Diese Funktion wird f¸r formatierte Textausgabe benötigt. Als Parameter erwartet printf() eine Formatzeichenkette. Wie genau eine formatierte Ausgabe realisiert wird, werden wie später besprechen. Das Zeichen "\" in der auszugebenden Zeichenkette nimmt eine Sonderstellung ein. Hinter diesem Zeichen werden Sonderzeichen angegeben die dann mit angezeigt werden sollen. Die Kombination "\n" bedeutet, daþ wir hier ein Return (Zeilenumbruch) ausgeben wollen.


Datentypen in C

So, nun kommen wir mal zu einem etwas schwierigeren, aber auch wichtigen, Teil der Sprache C: nämlich den Datentypen. Zuerst solltet ihr wissen was überhaupt Datentypen sind. Ganz einfach gesagt: ein Datentyp ist der Typ einer Variable bzw. er bestimmt den Typ der Werte die eine Variable aufnehmen kann. Nehmen wir mal zum Beispiel die Sprache ARexx. Dort kann man in eine Variable alles reinstopfen, egal um was für Daten es sich handelt, ob nun Zahlen, Fließkommazahlen oder Zeichenketten. In C ist das nun nicht möglich. Hier müssen wir genau festlegen was eine Variable für Daten aufnehmen kann. C-Anfänger verwenden immer nur vier Datentypen: nämlich Integer für Ganzzahlen, Float für Fließkommazahlen einfacher Genauigkeit, Double für Fließkommazahlen doppelter Genauigkeit und Character für Zeichen. Da wir aber schlaue C-Programmierer werden wollen und immer darauf bedacht sind, daß wir so wenig Speicher wie möglich nutzen, werden wir uns nicht auf diese drei Datentypen beschränken. Lange Rede kurzer Sinn, hier eine Liste mit den C-Datentypen:

Datentyp Größe Werte
char 8 Bit -128 bis +127
unsigned char 8 Bit 0 bis +255
short 16 Bit -32768 bis +32767
unsigned short 16 Bit 0 bis +65535
long 32 Bit -2147483648 bis +2147483647
unsigned long 32 Bit 0 bis +4294967295
float 16 Bit einfach genaue Fließkommazahl
double 32 Bit doppelt genaue Fließkommazahl
int 32 Bit hängt von der Größe ab

Die Größe des Integer-Typs hängt von der verwendeten Maschine ab, da Integer-Variablen in Prozessor-Registern verarbeitet werden (beim Amiga muß man das extra angeben). Bei den Prozessoren der 68000er-Familie (wie sie im Amiga sind) ist jedes Register 32 Bit groß, daher ist der Interger-Typ hier ebenfalls 32 Bit groß.


Die Funktion printf()

Da wir nun die Datentypen in C kennen, werden wir uns die Funktion printf() mal genauer anschauen. Wie ihr schon wisst, können wir damit formatierte Ausgaben realisieren. Im einfachsten Fall übergeben wir printf() nur den Ausgabestring. Wollen wir aber z.B. Variableninhalte ausgeben, müssen wir im Ausgabestring Platzhalter reservieren und zusätzlich noch die Variablen übergeben. Beginnnen wir mit ein paar Beispielen:

Beispiel Nr. 1
printf("Der Wert von a ist %d\n", a);

Der Platzhalter im Ausgabestring ist "%d". An diese Stelle kommt also der Wert der Variablen "a" im Formatstring.

Beispiel Nr. 2
printf("Die Summe von %d und %d ist %d\n", a, b, c);

Hier werden die Werte der Variablen "a", "b" und "c" ausgegeben.

Beispiel Nr. 3
printf("Der Typ unter deinem Tisch heisst %s\n", name);

In diesem Beispiel kommt an die Stelle des "%s" der Inhalt der Variablen "name".

Wie ihr seht können wir mit printf() schön formatierte Ausgaben realisieren. Aber printf() kann noch mehr. Die Platzhalter im Ausgabestring bestimmen zusätzlich noch wie die Daten ausgegeben werden. Hier erst einmal eine Übersicht, was die einzelnen Platzhalter bedeuten.

Zeichen Argument Umwandlung in
%d, %i int dezimal mit Vorzeichen
%o int oktal ohne Vorzeichen und führende 0
%x int hex. ohne Vorzeichen führendes x, mit abcdef
%X int hex. ohne Vorzeichen führendes X, mit ABCDEF
%u int dezimal ohne Vorzeichen
%c int einzelnes Zeichen (als unsigned char)
%s char * Zeichenkette
%f double dezimal als [-]mmm.ddd
%e double dezimal als [-]m.dddddde{+|-}xx
%E double dezimal als [-]m.ddddddE{+|-}xx
%g double je nach Genauigkeit wie %e oder %f
%G double je nach Genauigkeit wie %E oder %f
%p void * als Zeiger
%n int * Die Anzahl der bisher von diesem Funktionsaufruf ausgegebenen Zeichen wird im Argument abgelegt. Ein Argument wird nicht umgewandelt.
%% - Es wird kein Argument umgewandelt, sondern ein % ausgegeben.

Was es sich mit dem Zeichenketten und Zeigern auf sich hat, werde ich später genauer erklären. Schaut euch mal einen Beispiel-Quelltext für prinft() an.


Die Funktion scanf()

Was für formatierte Textausgabe printf() ist, ist für formatierte Eingabe scanf(). Der Aufbau der Formatzeichenkette ist der gleiche wie bei printf(), nur mit dem Unterschied, daß wir Daten in die Variablen eingeben. Wir müssen nur vor dem Variablennamen den Operator "&" setzen. Dies bedeutet dann, daß wir die Speicheradresse der Variablen angeben. Machen wir das nicht, ist der Wert der Variablen nach der Eingabe immer noch 0. Außerdem ist es besser für die Eingabe von dezimalen Zahlen immer den Variablen den Typ Interger zu geben, ansonsten ist der Wert der Variablen 0 oder das Programm stürzt ab (fragt mich aber nicht warum, ich muß mich da erstmal schlau machen). Zum besseren Verständniss hier noch ein paar Beispiele:

Beispiel Nr. 1
scanf("%d, &a);

Hier wird also die Eingabe eines Wertes für die Variable "a" erwartet. Der Wert ist dezimal.

Beispiel Nr. 2
scanf("%c", &a);

Hier ist der Wert nur ein einzelnes Zeichen.

Die Übersicht für die Platzhalter von printf() gilt auch für scanf(). Ihr könnt euch auch noch das Beispiel-Programm für scanf() anschauen.

Ok Leute, damit ist der erste Teil unseres C-Kurses leider schon zu Ende. Aber keine Sorge, in der 2. Ausgabe der AmigaToday findet ihr dann auch den zweiten Teil. Also dann, schönes programmieren.