ST Computer C-Kurs Teil 1

1. c??

Na, was soll C schon sein! Eine Programmiersprache natürlich, wie andere auch, werden viele jetzt sagen. Aber warum sollte man sich dann der Mühe unterziehen, C zu lernen, wenn es nichts Besonderes daran gibt? Genau diese Frage will ich versuchen, in diesem Kurs zu beantworten.

Zu einer Einführung gehört immer ein gewisser geschichtlicher Rahmen, um verständlich zu machen, woher eine bestimmte Idee oder ein Gedanke seine Triebkraft nimmt, die er braucht, um sich in weiten Kreisen durchzusetzen. Und durchgesetzt hat sich C schon allemal. Im sogenannten Home- und Personalcomputerbereich beginnt die Welle zwar erst langsam zu rollen, aber wenn erst einmal gewisse Hemmschwellen überwunden sind, wird sich C mit ziemlicher Sicherheit als die Standardsprache - gerade in dem oben genannten Bereich - etablieren. C wird wahrscheinlich PASCAL in den akademischen Bereich zurückdrängen und neben dem nicht totzukriegenden BASIC den kommerziellen und technisch-wissenschaftlichen Bereich beherrschen.

1.1 Entstehung

Die historische Entwicklung von C wurde zwar schon in der letzten Ausgabe der ST Computer kurz angerissen. Ich will aber trotzdem noch einmal auf einige wichtige Punkte eingehen.

C wurde in den 70er Jahren in den Bell Laboratories, einer Tochter des amerikanischen Telefongiganten AT&T, entwickelt. Es war von vornherein eine Sprache für Praktiker, von Praktikern. Geschaffen wurde C vor allem zur Entwicklung eines anderen Softwareprodukts, das sich trotz aller Unkenrufe zu dem Betriebssystem im Bereich der Mini- und Mikrorechner entwickelt hat. Ich spreche natürlich von UNIX. Zur weiten Verbreitung von UNIX hat nicht zuletzt die Sprache C beigetragen, da es durch sie erst möglich wurde, UNIX ohne große Verrenkungen auf andere Rechner, mit unter Umständen völlig anderen Prozessorstrukturen zu übertragen. UNIX ist zu über 90 % in C geschrieben (ca. 12 000 Zeilen Quelltext), und nur sehr zeitkritische Teile wurden in Assembler implementiert (ca. 800 Zeilen). Zusammen mit Unix hat C seinen Siegeszug angetreten. Praktisch alle unter Unix laufenden Programme, wie Editoren, Textverarbeitungssysteme, Dienstprogramme usw. wurden -und werden - in C geschrieben. GEM wurde übrigens auch zum Großteil in C geschrieben. C ist eine Sprache, die das ganze Spektrum - von der maschinennahen Programmierung, wie sie gerade für Betriebssysteme unabdingbar ist, bis hin zur Repräsentation kompliziertester Datenstrukturen, wie sie in Bereichen wie Datenbanktechnik, Übersetzerbau und CAD/CAM gebraucht werden - abdeckt. Ich hoffe, daß ich Sie damit neugierig genug gemacht habe, um mit dem Kurs richtig loslegen zu können.

Aber vor den ersten Beispielen noch ein paar technische Bemerkungen zum Kurs, zu den verfügbaren Compilern und zum ATARI.

2. Technisches

Falls Sie noch keinen C Compiler Ihr Eigen nennen können, werden Sie sich natürlich fragen, welcher der Beste ist. Diese Frage ist von meiner Warte aus nicht zu beantworten. Ich persönlich benutze seit Oktober 85 den Compiler der Firma Digital Research - das ist die Firma, von der GEM stammt. Dieser C Compiler ist im Entwicklungspaket der Firma ATARI enthalten und war der erste, der zum ATARI 520 ST( + ) angebogen wurde. Ich will an dieser Stelle nicht näher auf das Entwicklungspaket eingehen, da es schon verschiedentlich ausführlich beschrieben wurde. Nur soviel: Wer professionell Software für den ATARI erstellen will kommt (im Moment) nicht daran vorbei, die ca. 1000,- DM zu investieren. Das Paket enthält neben dem Compiler viele nützliche Utilities und eine beträchtliche Menge an Systeminformation (allein ca. 450 KByte Text auf Diskette). Außerdem gehört der Resource Construction Set dazu, der das Programmieren mit der GEM Oberfläche erst effizient macht. Man kommt zwar im Notfall auch ohne ihn aus, aber erspart viel Zeit - und noch mehr Nerven -falls man ihn hat. Der Compiler hält sich weitestgehend an den UNIX Standard und ist, zusammen mit einer genügend großen RAM-Disk, ganz angenehm zu benutzen. Die Version, die ich habe, hat allerdings einige Probleme mit manchen Funktionen aus der Standardbibliothek (scanf, malloc, calloc...). Wenn man der Gerüchteküche glauben darf, sind diese Fehler allerdings inzwischen behoben.

Ein anderer C Compiler ist GST C. Dieser Compiler wird wie das Entwicklungspaket mit Editor, Assembler und Linker geliefert. Das besondere an ihm ist, daß er eine Desktop-ähnliche Benutzeroberfläche bietet, die es gerade dem Anfänger leichter macht, mit dem Compiler zu arbeiten. Ich kenne dieses C zwar nur vom Anschauen, kann es aber trotzdem nur sehr eingeschränkt empfehlen. Aus Besprechungen weiß ich, daß ihm einige wichtige Sprachelemen-te, wie z. B. structs und die Datentypen long und float fehlen, außerdem soll es Probleme mit den dazugehörigen GEM Bibliotheken geben.

Es existieren noch einige andere Compiler. Es wäre sehr nützlich, wenn dazu vielleicht entsprechende Reaktionen aus dem Leserkreis kämen. Lattice C hat einen ganz guten Ruf - es ist das Standard C für GEM unter MS DOS.

Um bei diesem Kurs mitmachen zu können - und mitmachen sollten Sie, denn vom Zuschauen allein lernt man nichts - sollten Sie für den Anfang zumindest ein C mit einer halbwegs vollständigen Standardbibliothek haben (Tabelle 1.1 ist ein Ausschnitt aus der UNIX Bibliothek). Außerdem sollten Sie in irgendeiner Form die Betriebssystemaufrufe gemdos(), bios() und xbiosQ zur Verfügung haben. Für die späteren Folgen des Kurses brauchen Sie dann aber auch die GEM Bibliotheken (vdilib und aeslib).

Ein Ende des Kurses ist vorerst nicht abzusehen, er wird solange weitergehen, wie aus der Leserschaft Interesse bekundet wird und dem Autor etwas einfällt.

         
fclose feof ferror fgetc fgets
fopen fprintf fputc fputs fread
freopen fscanf fseek ftell fwrite
getc getchar gets getw mktemp
pclose popen printf putc putchar
puts putw rewind scanf setbuf
sprintf sscanf tempnam tmpfile tmpnam
ungetc isalpha isupper islower isdigit
isxdigit isalnum issapace iscntrl ispunct
isprint isgraph »isascii toupper tolower
toascii atoi atol strtol atof
ecvt gcvt l3tol ltol3 strcat
strncat strcmp strncmp strcpy strncpy
strlen strchr strrchr strpbrk strcspn
strtok malloc calloc realloc free
sin cos tan atan log

usw.

Tabelle 1.1: Einige Funktionen aus der C Standard Bibliothek

3. Jetzt geht’s los

3.1 Ein erste Beispiel

Das erste Beispiel ist, wie kann es anders sein, folgendes:

		/* ST Computer C-Kurs Beispiel 1.1	*/

		main()
		{
			printf(".Hier spricht dein ATARI\n");
		}

Obwohl es ziemlich einfach aussieht: Es steckt doch schon eine ganze Menge C darin. Sollten Sie noch nie ein C Programm geschrieben haben, tippen Sie es ab und versuchen Sie, es zu übersetzen. Wahrscheinlich lernen Sie eine Menge über den Umgang mit Ihrem speziellen C System.

Für die erste Zeile interessiert sich Ihr C Compiler kurioserweise überhaupt nicht, dagegen ist sie für Sie und alle, die sich mit ihrem Programm beschäftigen müssen, äußerst wichtig. Es handelt sich nämlich um einen Kommentar. Alles was zwischen /* und */ steht, wird vom Compiler ignoriert. Kommentare dürfen überall dort stehen, wo auch Leerzeichen, Tabs oder Zeilen-» enden stehen können. Sie sollten sich von Anfang an einen Kommentierungsstil angewöhnen und diesen konsequent beibehalten. Ein einzeiliger Kommentar oder ein Kommentar nach einer Anweisung sollte aussehen wie im obigen Beispiel. Mehrzeilige Kommentartexte schreibt man z. B. so:

/* *Dies ist ein mehrzeiliger *Kommentar. Sie sollten einen *einheitlichen Stil für Kommentare *wählen. */

oder man malt ein schönes Kästchen aus Sternchen um den Kommentar.

Als nächstes fallen als typisches Merkmal von C die geschweiften Klammern auf ({,}). Sie umschließen immer irgendeine Art von Einheit oder im Fachslang einen Block. In unserem Fall ist dieser Block der Anweisungsteil einer Punktion und zwar der wichtigsten Punktion eines C Programms. Die Funktion hat den Namen **main()+*. Die Klammern sagen dem Compiler, daß main der Name einer Funktion und nicht etwa einer Variablen oder etwas anderem ist. Innerhalb der Klammern können einer Funktion Werte (Argumente, Parameter) übergeben werden, mit denen sie (die Funktion) irgendetwas machen soll. Dazu aber später mehr. Wichtig ist nur: Auch wenn die Funktion keine Argumente hat, die Klammern - und zwar weder eckige noch geschweifte, sondern runde — müssen geschrieben werden.

Warum ist main denn so wichtig? - Jedes Programm muß ja irgendwo anfangen und C Programm fängt immer bei der Funktion main an. Sie ist also so etwas wie ein Hauptprogramm in PASCAL. Außer der Tatsache, daß ein Programm bei main anfängt, ist aber nichts besonders an dieser Funktion, und alle im weiteren Verlauf aufgezählten Regeln für Funktionen gelten für sie genauso.

Funktionen sollen im Normalfall etwas tun und haben deshalb einen Anweisungsteil, der, wie oben schon erwähnt, zwischen { und } geklammert wird.

Während der Entwicklung eines Programms kann es aber durchaus nützlich sein, Funktionen zu haben, die nichts tun, deshalb darf der Anweisungsteil auch leer sein.

Wozu brauche ich das, werden sich jetzt einige fragen. Ganz einfach! Normalerweise entwickelt man ein Programm vom „Groben“ ins „Feine“; im angelsächsischen Sprachraum heißt das Top-Down-Entwurf. Wenn man bei einigen Funktionen nicht genau weiß, wie man sie realisieren soll, läßt man sie zuerst einmal einfach „leer“. Das hat den Vorteil, daß man das Programm übersetzen und linken kann, ohne daß Fehler gemeldet werden. Bei der späteren Verfeinerung füllt man die Funktionen dann nach und nach mit Leben.

Bei unserem Beispiel ist der Anweisung-steil aber nicht leer, sondern enthält einen Funktionsaufruf. Wissen Sie noch, woran man erkennt, daß es sich um eine Funktion handelt? Richtig, an den runden Klammern. Die Funktion hat den Namen printf und bekommt ein Argument mit auf den Weg. Bevor wir uns das Argument näher ansehen, ein paar Worte zu printf. printf ist eine Funktion aus der Standardbibliothek von C, das heißt, jedes C System sollte diese Funktion in seiner Bibliothek haben. Haben Sie den feinen Unterschied bemerkt? Ich sagte nicht C Compiler sondern C System, printf hat nämlich mit dem Compiler und damit mit dem Sprachumfang von C nicht das Geringste zu tun! Es ist eine Funktion wie alle anderen auch und sehr wahrscheinlich vom Entwickler des Systems auch in C geschrieben worden. Es ist eine Besonderheit von C, daß es praktisch keine Funktionen, welcher Art auch immer, im Sprachumfang gibt. Ein nackter C-Compiler wäre deshalb auch ziemlich uninteressant, wenn er nicht zusammen mit einer Standardbibliothek von Funktionen geliefert würde. Der Umfang dieser Standardbibliothek ist ein wichtiges Beurteilungsmerkmal für C Systeme.

Eine Bemerkung am Rande: Praktisch alle existierenden C Compiler unterscheiden Klein- und Großbuchstaben. Kommen Sie also nicht etwa auf die Idee, PRINTF oder Printf zu schreiben, Sie ernten sonst nur ein verständnisloses Kopfschütteln vom Linker.

Wie man vom Namen her schon vermuten kann, gehört printf zum Ausgabeteil der C Funktionen. Die Funktion erlaubt Ihnen so ziemlich alles, was sich drucken läßt, auf den Bildschirm auszugeben - Zeichen, Zahlen (dezimal, hexadezimal oder oktal), Zeichenketten, Adressen usw. In unserem Beispiel ist es eine Zeichenkette oder wie es in C heißt, ein String. Strings erkennt der Compiler daran, daß sie zwischen doppelten Anführungszeichen (Double Quotes) stehen. Beim Übersetzen hängt der Compiler an jeden String ein Nullzeichen, das ist das ASCII Zeichen mit dem Wert 0, an. Auf diese Weise erkennen Bibliotheksfunktionen und die Funktionen, die Sie vielleicht selbst schreiben werden, das Ende des Strings „Hier spricht Ihr ATARI n“ noch unklar sein dürfte, ist dieses seltsame n. Das ist die Art, wie man dem Compiler mitteilt, daß alles Weitere am Anfang einer neuen Zeile passieren soll. Am besten, Sie probieren einfach einmal, was passiert, wenn Sie das n weglassen. Es gibt noch weitere Zeichen, die Sie auf diese Art in Strings einfügen können. Schauen Sie sich dazu Tabelle 1.2 an.

\n	Zeilenvorschub
\t	Tabulatorzeichen
\b	Backspace
\r	Carriage Return
\f	Seitenvorschub
\0	Nullzeichen
\\	Backslash
\"	Hochkomma
\xxx	ASCII Zeichen im Oktal Code (0< = X< = 7)

Tabelle 1.2 C Fluchtsymbole

Für den ATARI gibt es aber eine noch weitaus komfortablere Methode, Steuerzeichen zu drucken, dazu gleich mehr. Nur noch eine letzte Bemerkung zu Beispiel 1.1. Vielleicht ist es Ihnen gar nicht aufgefallen: am Ende der printf-Anweisung steht ein Semikolon. Falls

Sie es vergessen haben, hat sich Ihr Compiler mit einer mehr oder weniger verständlichen Fehlermeldung darüber erbost (am wahrscheinlichsten hat er etwas von '...missing;’ gemurmelt). In C gehört an das Ende jeder Anweisung ein Semikolon. Das bedeutet also, das Semikolon beendet eine Anweisung, im Gegensatz zu PASCAL, wo ein Semikolon zwei Anweisungen trennt. Überlegen Sie sich mal in Ruhe, wo der Unterschied liegt.

3.2 Eine nützliche kleine Funktion

Einen Nachteil des ersten Beispiels werden Sie sicher schon bemerkt haben. Je nachdem, ob Sie Ihrem Programm die Endung .tos oder .prg verpaßt haben, blitzt die Nachricht entweder nur kurz auf dem Schirm auf und das Desktop meldet sich sofort wieder, oder der Text wird irgendwo auf den Bildschirm mitten übers Desktop geschrieben. Diesem Problem werden wir nun auf die Sprünge helfen uij(d gleich eine nützliche kleine C Funktion fabrizieren, die Sie in Ihren eigenen Programmen verwenden können. Hier erst einmal die Funktion:

/* ST Computer C-Kurs Beispiel 1.2	*/

#include "vt52.h"

#define Wait()	gemdos(0x01)

main()
{
	warte_taste();
}

/*
 * Funktion gibt Text 'Bitte beliebige Taste’ in der letzten
 * Bildschirmzeile ganz links aus. Der Text wird weiss auf schwarz
 * gedruckt.
 */

warte_taste()
{
	printf( CURS_LOC, 32+24, 32+0); 
	printf( REV_ON );
	printf( " Bitte beliebige Taste! " ); 
	printf( REV_OFF ); 
	Wait();
}

/* Ende Beispiel 1.2	*/

Sie gibt in der letzten Bildschirmzeile den Text „Bitte beliebige Taste“ aus, und zwar weiß auf schwarz, und wartet auf einen Tastendruck.

Zu diesem Beispiel gibt es wieder eine Menge interessanter Bemerkungen zu machen. Als ersten Unterschied zum letzten Beispiel entdeckt man sofort die seltsamen Nummernzeichen # in der zweiten und vierten Zeile. Diese sind ein Zeichen dafür, daß an diesen Stellen der C-Preprocessor auf den Plan tritt. Der Preprocessor ist etwas, wie jedem alten Lateiner sofort an der Vorsilbe ’pre’ klar wird, was vor dem eigentlichen Compiler abläuft. Es ist ein Programm, das hauptsächlich irgendwelche Textersetzungen vornimmt. Was der Preprocessor so alles kann, werden wir in den kommenden Folgen des Kurses noch zur Genüge bewundern. Im Moment will ich nur kurz die zwei Kommandos im Beispiel erläutern.

Die #include Anweisung liest eine Datei mit Namen vt52.h ein und setzt deren Inhalt textuell genau an die Stelle, an der das Kommando stand. Wenn danach der Compiler läuft, bemerkt er gar nicht, daß in der zweiten Zeile einmal eine include Anweisung stand, weil er statt ihrer den Inhalt der eingelesenen Datei vorfindet. Die Anweisung ist übrigens schachtelbar, das heißt in der eingelesenen Datei darf wieder ein # include stehen usw. Die maximale Schachtelungstiefe hängt vom jeweiligen Preprocessor ab.

Die #define Anweisung bewirkt auch einen Textersatz, und zwar wird im nachfolgenden Programm an jeder Stelle, an der ’Wait()’ steht, der Text ’gemdos(0x01)’ eingesetzt. - Für die ganz Peniblen: einzige Ausnahme; wenn Wait() in Double Quotes stehen würde, also innerhalb eines Strings, würde es nicht ersetzt werden.

Mit # define kann man also Konstanten definieren oder Betriebssystemaufrufe, wie gemdos (Funktionsnummer, Argumente), lesbarer machen. Die ausgiebige Benutzung dieser Möglichkeit macht ein Programm leichter lesbar und, ws noch wichtiger ist, leichter änderbar.

Noch eine letzte Bemerkung zum Preprocessor: Alle Kommandos, die für ihn bestimmt sind, müssen in der ersten Spalte einer Zeile beginnen!

Wenn Sie sich die Datei vt52.h anschauen, werden Sie feststellen, daß Sie außer Kommentaren nur Anweisungen an den Preprocessor enthält. Diese Anweisungen haben etwas mit der angekündigten Methode, auf dem ATARI Steuerzeichen zu drucken, zu tun. Ein VT52 ist ein Terminal, wie man es an Mini- und Großrechnern finden kann. Es ist schon ziemlich alt und nicht besonders leistungsfähig, aber aus diesem Grund auch einfach zu simulieren (im Jargon heißt das emulieren). Der Atari emuliert nun im Bereich seiner Zeichen-ein/ausgabe so ein VT52 Terminal. Das VT52 kennt bestimmte Steuersequenzen, die alle mit dem Zeichen Escape (ASCII 27) beginnen, um z. B. den Cursor über den Bildschirm zu steuern. Wenn man beispielsweise das Zeichen Escape schickt und gleich danach ein großes A, springt der Cursor eine Zeile nach oben. In der Datei vt52.h sind nun alle auf dem ATARI möglichen Steuersequenzen gesammelt und mit Hilfe des Preprocessors auf leicht zu merkende Namen abgebildet worden. Experimentieren Sie doch einfach etwas. Mit den Escapesequenzen lassen sich auf einfache Weise schon sehr anständige Bildschirmdialoge aufbauen, ohne daß man gleich tief in GEM einsteigen muß.

Das Beispiel besteht diesmal aus zwei Funktionen. main() kennen Sie schon, und danach kommt eine Funktion warte_taste(), die Sie exakt so in eigene Programme übernehmen können. Der Aufbau des Beispiels ist typisch für ein C Programm. Es besteht aus einer Reihe von Funktionen, die sich gegenseitig (auch rekursiv) aufrufen können. Im Gegensatz zu PASCAL dürften Funktionen nicht geschachtelt werden. Zwischen den Funktionen dürfen noch globale Daten- und Typdefinitionen stehen.

In der Funktion warte_taste wird zuerst der Cursor in die letzte Bildschirmzeile gesetzt. Dazu wird mit printf die entsprechende Escapesequenz ausgedruckt. 033 ist eine Oktalzahl, d. h. eine Zahl zur Basis 8 (3 ★ 8 + 3 = 27), also genau der Code für Escape.

Auf diese Weise können Sie jedes Steuerzeichen in einen String bekommen -007 ist z. B. das Zeichen für die Bell; wenn es ausgegeben wird, gibt der Rechner normalerweise Laut.

/*
*	Include File vt52.h
*	erstellt 26.03.86 von Th. Ueinstein
*	ermoeglicht UT52 Escapesequenzen in C Programmen zu verwenden.
*
*	Verwendung:
*		print( HOME );
*	setzt z.B. den Cursor ganz oben in die linke Ecke.
*		print( CURS_LOC, 32+12, 32+10 );
*	setzt den Cursor in die 12. Zeile und 10. Spalte.
*/

#define	C_UP		"\033A"	/*	Cursor eine Zeile nach oben	*/
#define	C_DOWN		"\033B"	/*	"	’*	*’	H unten	*/
#define	C_RIGHT		"\033C"	/*	" Spalte	nach rechts	*/
#define	C_LEFT		"\033D"	/*	"	eine ’*	" links	*/
#define	CLEAR_HOME	"\033E"	/*	Bildschirm loeschen, Cursor home */
#define	HOME		"\033H"	/*	Cursor home	*/
#define	SCROLL_UP	"\033I"	/*	Cursor Zeile nach oben, wenn ganz*/

/* oben wird Bildschirm gescrollt */ 
#define	CLEAR_DOWN	"\033J"	/*	Bildschirm ab Cursorposition ein-*/
							/* schliesslich loeschen */
#define	DEL_EOL		"\033K"	/*	Loescht ab Cursor bis Zeilenende */
#define	INS_LINE	"\033L"	/*	Fuegt unter Cursor neue Zeile ein*/
#define	DEL_LINE	"\033f1"	/*	Loescht Cursorzeile. Unterste */
								/* Zeile wird Frei */
#define	CURS_LOC	"\033Y%c%c"	/*	Setzt Cursor Beispiel s.o. */
#define	CHAR_CCL0R	"\033b*c"	/*	Setzt ZeichenFarbe */
								/* Monochrom weiss-’0’, schwarz-’ 1’*/
								/* Farbe ’A* bis ’F* Je nach Auf-*/
								/* loesung.	*/
#define	BG_COLOR	"\033c%c"	/*	wie oben aber HintergrundFarbe */
#define	C_ON		"\033e"		/*	Cursor einschalten */
#define	C_GFF		"\033F"		/*	Cursor ausschalten	*/
#deFine	C_SAUE		"\033j"		/*	Cursorposition speichern	*/
#define	C_RESTORE	"\033k"		/*	Cursor auf gesp. Position	*/
#define	ERASE_L		"\0331"		/*	Loescht Cursorzeile Zeile bleibt */
#define	DEL_BOL		"\033o"		/*	von Zeilenanf bis Cpos loeschen */
#define	REV_ON		"\033p"		/*	Reverse ein	*/
#define	REV_OFF		"\033q"		/*	Reverse aus	*/
#define	WRAP_ON		"\033v"		/*	Automatischer Zeilenueberlauf ein*/
#define	WRAP_OFF	"\033w"		/*	Automatischer Zeilenueberlauf aus*/

/* Ende von vt52.h */

/* ST Computer C-Kurs Beispiel 1.3	*/

#include "vt52.h"

#define	MAXZAHL	1000	/*	Maximale zu erratende Zahl */
#define	MAX_VERSUCHE 20	/*	Soviel Versuche hat Spieler*/
#define	Get key()	gemdos(1)	/*	Liest Zeichen von Tastatur */
#define	Random()	xbios(17)	/*	lieFert 16 Bit ZuFallszahl */

main()
{
	int	n;			/*	Anzahl der Rateversuche des Spielers	*/
	int	versuch;	/*	Zahl, die Spieler eingegeben hat	*/
	int	geheim;		/*	Zu erratende Zahl	*/
	char c;

	do (

n = versuch = 0; 
geheim = neue_zahl();

printf("\n\nIch denke mir eine Zahl zwischen 1 und %d\n", MAX_ZAHL);

while ((versuch != geheim) && (n < MAX_VERSUCHE)) {

	versuch = Frage_spieler(); 
	n++;
	if (versuch < geheim)
		putsC "%nMeine Zahl ist groesser" );
	else
	if (versuch > geheim)
		puts( "%nMeine Zahl ist kleiner" );
	else {
		printf( REV_ON );
		printf( "\n Hurra, Sie haben sie !	");
		printf( "Das waren %d Uersuche\n", n); 
		printf( REV_OFF );
			}
		} /* while */

		if (n == MAX_VERSUCHE)
				printf( "Meine Zahl war %d\n", geheim);
		printf( "Noch ein Spiel? (J/N)");

	} while ((( c = Get_key() ) == 'J') || ( c == 'J'));

} /* main */

int neue_zahl(!
{
	unsigned int rnd; 
	
	rnd = Random();
	return( ( rnd % MAX_ZAHL ) + 1 );
}

int Frage_spieler()
{
	int zahl;

	printf( "Die heisst meine Zahl? <Leertaste> "); 
	scanf( "%d", &zahl); 
	return( zahl );
}

/* Ende Beispiel 1.3*/

Danach kommt ein großes Y als Zeichen, daß der Cursor gesetzt werden soll. Als nächstes folgen zwei Zeichen, das erste bestimmt die Zeile, das zweite die Spalte, in die der Cursor soll. Zur Zeilen- und Spaltennummer muß man jeweils 32 dazuaddieren. Diese Aufgabe erledigt die printf-Funktion. Ihr allgemeines Format ist:

printf (Kontrollstring, Argument 1, Argument2, Argumentn)

Die Zeichen im Kontrollstring werden einfach ausgedruckt, solange es keine %-Zeichen sind. Das %-Zeichen leitet ein sogenanntes Formatelement ein. In Beispiel 1.2 folgt den zwei %-Zeichen jeweils ein ’c’, das bedeutet, daß das zugehörige Argument als einzelnes Zeichen zu interpretieren ist. Zum ersten %c gehört also 32 + 24 zum zweiten 32 + 0. Daraus folgt die einfache Regel, daß zu jedem %-Zeichen im Kontrollstring ein (passendes) Argument in der Argumentliste stehen muß. Im weiteren Verlauf des Kurses werden Sie noch mehr Formatelemente kennenlernen. Für eine vollständige Auflistung muß ich Sie allerdings an die zahlreichen Bücher zum Thema C verweisen.

Was im Beispiel weiter passiert, werden Sie nun sicher ohne weitere Erklärungen verstehen. Nur noch eins: die Funktion gemdos() ruft das Betriebssystem des ATARI auf, als Argument wird die Nummer der gewünschten Funktion übergeben. In unserem Fall 0x01 - das ist die C Schreibweise für die Hexadezimalzahl 1 -. Dieser Betriebssystemaufruf wartet solange, bis eine Taste gedrückt wird und liefert dann das entsprechende Zeichen zurück. Wir interessieren uns aber momentan nicht für das Ergebnis, sondern uns kommt es nur auf das Warten an, deshalb habe ich die Funktion kurzerhand Wait() genannt.

Versuchen Sie ruhig einmal, das Beispiel etwas abzuändern. Lassen Sie sich z. B. die Aufforderung in der ersten Zeile ausgeben.

4. Ein größeres Beispiel

Das Beispiel 1.3 ist schon etwas länger. Es ist ein kleines Spiel, das übrigens bereits in der letzten Ausgabe im PASCAL-Kurs aufgetaucht ist, so haben Sie die Möglichkeit, zu vergleichen.

Der Rechner 'denkt’ sich eine Zahl in einem bestimmten Intervall und Sie sollen sie erraten.

Ganz zu Beginn finden Sie wieder einige Anweisungen an den Preprocessor. Die Obergrenze der zu erratenden Zahlen wird z. B. MAX_ZAHL genannt, wollen Sie einen anderen Zahlenbereich, müssen Sie nur die 1000 ersetzen und der Rest des Programms bleibt unverändert.

Das Programm besteht weiter aus drei Funktionen: Wie immer main(), dann neue_zahl() und frage_spieler().

An neue_zahl() kann man gut den allgemeinen Aufbau einer C Funktion sehen:

Typ Funktionsname(formale Parameter) Parameterdeklaration

lokale Variable

Anweisungsteil

Typ ist dabei der Datentyp, der durch die Funktion zurückgeliefert werden soll. Bei der Funktion neue_zahl ist das ein int-Wert, d. h. ein ganzzahliger Wert. In C besteht übrigens die Vereinbarung, daß eine Funktion, bei der kein Rückgabewert angegeben wird, einen int-Wert liefert, man hätte bei neue_zahl das int also einfach weglassen können.

Da die Funktion keine Argumente hat, braucht man auch keine Argumentdeklaration. Beachten Sie außerdem bitte, daß nach name() kein Semikolon stehen darf. Das ist ein sehr häufiger Fehler. Wenn Ihr Compiler sich einmal über eine falsche externe Deklaration beschweren sollte, ist das ein sicheres Anzeichen dafür, daß Sie nach einem Funktionsaufruf ein Semikolon gesetzt haben.

Innerhalb des Funktionsrumpfes ... wird zuerst eine lokale Variable rnd definiert (Lokal heißt im Gegensatz zu global, daß die Variable außerhalb von neue_zahl nicht bekannt ist. Die Variable hat den Typ unsigned int, das heißt, es ist eine vorzeiche'nlose Ganzzahl (0...2I6-1). In diesem Zusammenhang ist es wiederum erlaubt, das int wegzulassen und nur unsigned rnd zu schreiben.

In der nächsten Zeile wird rnd der Rückgabewert des Betriebssystemaufrufs Random() übergeben. Durch (rnd %MAX_ZAHL)+ 1 wird rnd auf den Bereich 1... MAX_ZAHL abgebildet. % ist die Modulo-Funktion (Beispiel: 10 % 3 liefert 1, d. h. den Rest, der bei Teilung von 10 durch 3 bleibt).

Return zeigt dem Compiler an, daß er aus der Funktion zurückkehren und als Ergebnis das Resultat des nachfolgenden Ausdrucks liefern soll. Das allgemeine Format ist:

return (Ausdruck) oder
return Ausdruck

Die Klammern sind nur zur besseren Lesbarkeit da und können einfach weggelassen werden. Wer meint, er hätte eine in C eingebaute Funktion entdeckt, sieht sich also enttäuscht.

Was die Funktion frage_benutzer() bewirkt, können Sie sich jetzt schon leicht selbst überlegen. Einzige Neuigkeit ist die Funktion scanf. Sie ist das Gegenstück zu printf, d. h. man kann mit ihr Werte von der Tastatur einiesen. Was das Zeichen & bedeutet, verrate ich im Moment noch nicht. Falls Sie es weglassen, werden Sie mit an Sicherheit grenzender Wahrscheinlichkeit von Ihrem ATARI mit 2 kleinen Bomben belohnt.

Die Funktion scanf funktioniert, wie oben bereits erwähnt, beim C Compiler des Entwicklungspakets nicht richtig. Eine Eingabe läßt sich nicht mit der Returntaste abschließen. Sollte sie bei Ihrem C funktionieren, können Sie natürlich die Aufforderung „ < Leertaste >“ weglassen.

Kommen wir zum größten Happen des Beispiels, in der main() Funktion steckt diesmal allerhand.

Zuerst werden wieder einige lokale Variablen definiert. Neu ist dabei der Datentyp char. In eine Variable dieses Typs paßt genau ein Zeichen (1 Byte). Man kann also den Zahlenbereich von - 128 ... 127 darstellen. Wenn man den Bereich von 0 ... 255 will, muß man den Typ als unsigned char angeben. Viele C Compiler kennen allerdings keine vorzeichenlosen char-Werte.

Im Anweisungsteil von mainQ stoßen wir jetzt zum ersten Mal auf einige der Kontrollstrukturen - das sind Anweisungen zur Verzweigung und Schleifensteuerung - die C zu bieten hat.

Das Spie! soll solange weitergehen, wie der Spieler auf die Frage „Noch ein Spiel (J/N)“ mit ’j’ oder ’J’ antwortet. Das erledigt eine sogenannte do-Schleife. Ihr allgemeines Format ist:

do
	Anweisung
while (Bedingung)

Anweisung wird dabei solange ausgeführt, bis die Bedingung falsch wird. Diese Form der Schleife entspricht dem Repeat ... until in PASCAL. Die Anweisung in unserem Fall ist gleich wieder ein ganzer Block ({...}). Zuerst werden die Variablen n und versuch auf 0 initialisiert. In C darf man das so wie gezeigt schreiben, da jede Zuweisung einen Wert liefert, den man dann weiter zu weisen kann (Zuerst wird der Wert 0 an versuch zugewiesen. Das Ergebnis ist natürlich 0, was dann an n zugewiesen wird).

Nachdem eine neue Geheimzahl errechnet und der Benutzer zur Eingabe aufgefordert wmrde, beginnt eine neue Schleife, die solange ausgeführt wird, wie der Benutzer die Zahl noch nicht erraten hat und außerdem die Anzahl der Rateversuche kleiner als MAX_VERSUCHE ist. Diesmal wird eine while-Schleife verwendet. Ihre allgemeine Form ist:

while (Bedingung) 
	Anweisung

Der Unterschied zur do-Schleife besteht darin, daß die Bedingung gleich zu Beginn der Schleife getestet wird. Ist sie von Anfang an falsch, wird die Anweisung nie ausgeführt, während die Anweisung in der do-Schleife immer mindestens einmal ausgeführt wird.

Innerhalb der while-Schleife muß der Spieler eine Zahl eingeben, die Anzahl der Versuche wird um eins erhöht -auf die typischen C Kurzoperatoren wie + + werde ich in der nächsten Folge genauer eingehen - dann wird mit zwei if-Anweisungen getestet, ob die eingegebene Zahl kleiner, größer oder gleich der Geheimzahl ist. Die if-Anweisung hat die gleiche Bedeutung wie in anderen Programmiersprachen auch. Ihre allgemeine Form ist:

if (Bedingung)
	Anweisung
else
	Anweisung

Der else-Teil kann dabei weggelassen werden. Dabei ist allerdings zu beachten, das bei verschachtelten if-Anweisungen der else-Teil immer auf das letzte if ohne else bezogen wird, falls diese nicht durch {if...} geklammert ist.

So, damit ist die erste Folge des C Kurses beendet, verdauen Sie erst einmal alles Neue und, was ganz wichtig ist, versuchen Sie selbst, kleine Funktionen zu schreiben. Mit dem bis jetzt Gelernten sollten Sie z. B. ohne weiteres in der Lage sein, das letzte Beispiel so zu ergänzen, daß der Computer eine Zahl von Ihnen errät. - Na wie wär’s?

Die Dinge, die ich im letzten Beispiel noch nicht erklärt habe, wie die Bedingungen und Operatoren, werde ich in der nächsten Folge ausführlich behandeln. Sie sollten aber aus dem Ablauf des Spiels selbst erschließen können, was z. B. && bedeutet.

Falls Sie Kritik oder Anregungen haben, schreiben Sie ruhig.


Thomas Weinstein
Aus: ST-Computer 05 / 1986, Seite 66

Links

Copyright-Bestimmungen: siehe Über diese Seite