Abgekoppelt - Von Basic nach C, Teil 1

C gewinnt ständig an Bedeutung, denn diese Sprache ist universell, in hohem Maße portabel, klar strukturiert und systemnah. Wenn Sie bereits Basic beherrschen, fällt der Umstieg nicht schwer.

C-Programme, die Sie auf einem Atari ST/TT entwickeln, lassen sich leicht auf andere Betriebssysteme wie z.B. MS-DOS übertragen. Ein optimierender C-Compiler gibt sich in Sachen Geschwindigkeit nur noch gegen ein gut geschriebenes Assembler-Programm geschlagen. Basic, besonders »GFA-Basic«, zählt auf dem Atari ST zu den leistungsstarken, strukturierten und schnellen Sprachen. Leicht zu erlernen und schnell zu programmieren - das sind Vorzüge, die Basic allen anderen Sprachen voraus hat. Bietet GFA-Basic durch eine sehr umfangreiche Befehlsliste einerseits eine komfortable Programmierumgebung, so hemmen andererseits gerade die komplexen Befehle das Verständnis. Wer versteht beispielsweise, welche Operationen notwendig sind, um einen String mit einem zweiten zu verknüpfen? C-Programmierer stehen daher oft skeptisch der Programmiersprache Basic gegenüber, zumal ihr immer noch der Mythos anhaftet, zu unstrukturiertem Programmierstil zu verführen. Zudem ist GFA-Basic nicht sehr kompatibel. Lediglich auf dem Atari ST und dem Commodore Amiga gibt es diesen Dialekt. Neuerdings versucht GFA Systemtechnik auch im Bereich MS-DOS mit ihrem Basic Fuß zu fassen. Dennoch existieren im Sprachumfang der GFA-Basic-Systeme große Inkompatibilitäten bei der Syntax, die eine Nachbearbeitung der Atari-Programme auf den anderen Computern erfordern. Ein weiterer Schwachpunkt von GFA-Basic ist die mangelhafte Modularisierung größerer Programme, wie sie beispielsweise die Sprachen C, Pascal oder Modula bieten.

Programmieren noch dem Parallelkonzept

In diesem C-Kurs stellen wir eine völlig neue Methode vor, C zu erlernen und in C zu programmieren: PBOC - »Programming in Basic and Optimizing in C«. Wir lernen C, indem wir all unser Basic-Wissen verwenden. Der Vorteil dieser Methode ist, daß Sie einerseits viel schneller C lernen und andererseits weniger Fehlern zum Opfer fallen, bzw. Fehler besser erkennen. In diesem Sinne richtet sich dieser Kurs hauptsächlich an diejenigen, die schon Erfahrungen in einer Computersprache besitzen bzw. an diejenigen, die den ersten C-Kurs der TOS (Ausgaben 5/90 bis 10/90) aufmerksam verfolgt haben. Als tatkräftige Unterstützung dieses Kurses bietet die TOS-Diskette eine Demoversion des Programmes »Basic Konverter nach C« der Firma Cicero-Software. Mit Hilfe dieses Programms lassen sich leicht parallele Strukturen von GFA-Basic und C durchschauen und üben. Sollten Sie einmal nicht wissen, wie Sie eine Routine in C umsetzen, so konvertieren Sie einfach Ihr Listing mit dem Programm. »Basic nach C« zeigt Ihnen immer die korrekte Syntax, damit Sie nicht erst umständlich in Büchern danach suchen müssen. Freilich verfügt erst die Vollversion über den gesamten GFA-Basic-Wortschatz.

Wir beziehen uns in diesem Kurs auf den Basic-Dialekt GFA-Basic und den ANSI-C-Compiler Turbo C«. Dies bedeutet nicht, daß unsere Methode nur mit diesen Produkten erfolgreich ist. in den einzelnen Teilen stellen wir Ihnen verschiedene Aufgaben. Diejenigen unter Ihnen, die alle Aufgaben korrekt lösen und an die Redaktion senden, nehmen an einer Verlosung teil. Als Preise winken drei »Basic Konverter nach C«, die uns die saarländische Firma Cicero-Software freundlicherweise zur Verfügung stellt.

In medias res

Unser erstes Beispiel zeigt die Grundstruktur eines Programms in beiden Sprachen. Zunächst GFA-Basic:

'Beispiel 1: Rechnen
'
a=SIN(0.25) + SQR(2)+2^3*4
PRINT a
≈ INP(2)

Nachdem wir diese Zeilen im Interpreter eingegeben haben, startet das Programm per Tastendruck und wir erhalten sofort das Ergebnis der Berechnung. GFABasic benötigt keinen Rumpf, das heißt keine Anweisungen vor oder nach den obigen Zeilen. Jetzt ein vergleichbares Programm in Turbo C:


#include 
#include 

double aD;

main(void)
{
	/* Beispiel 1 : Rechnen */
	aD = sin(0.25) + sqrt(2) +pow(2, 3) * 4;
	printf("%G\n", aD);
	getchar();
	return(0);
}

Diese Zeilen geben wir in den Editor von Turbo C ein, compilieren und linken den Quelltext unter Verwendung der Projekt-Datei »default.prj«. Starten wir das kleine Programm, so erhalten wir das gleiche Resultat wie oben. Vergleichen wir die beiden Listings, so erkennen wir auf Anhieb Ähnlichkeiten, Übereinstimmungen und auch Unterschiede. »/* ... /« entspricht in C einer REM-Markierung in Basic bzw. den Operatoren »‘« oder »!«. Die Anweisung »#include <stdio.h>« teilt dem C-Compiler mit, daß er eine Datei mit Namen »stdio.h« (Standardbibliothek für Ein- und Ausgaben) zuladen soll. Bibliotheken melden grundlegende Funktionen an und sind durch die Dateiendungen ».LIB« gekennzeichnet. Der Linker hängt diese nach dem Compilieren an Ihre Programme an. Hierzu gehört auch die Funktion »printf()« - das Pendant zum Basic-Befehl »PRINT«. Anhand dieser Anmeldungen (Deklarationen) stellen C-Compiler fest, ob der Aufruf dieser Funktion korrekt erfolgt. Diese Fehlerüberprüfung heißt auch Typprüfung.

	main(void)
	{
		... /* Hier stehen weitere Anweisungen */
	}

Hier handelt es sich um die Definition einer Funktion. Man merke sich, daß hinter einer Definition (Vereinbarung) einer Funktion kein Semikolon stehen darf, ansonsten trennen Semikolons in C meist Anweisungen. Jede Funktion muß in C definiert, das heißt irgendwo vorhanden und für den Compiler angemeldet sein. Die Anweisung »{ ... }« beschreibt in C einen sogenannten Block. Innerhalb eines Blocks sind bestimmte Operationen erlaubt, die wir weiter unten beschreiben. Der hier angegebene Block gehört zu einer Funktion mit Namen »main«. In jedem C-Programm muß eine Funktion »main« existieren. Wo diese Funktion tatsächlich steht, ist unerheblich. Alle Programmzeilen in GFA-Basic, die nicht innerhalb einer Prozedur oder Funktionen stehen, muß man in C innerhalb von »main« schreiben. Zur Formatierung: Hinter jedem Funktionsnamen erwartet der C-Compiler immer zwei runde Klammern. Erfolgt der Funktionsaufruf ohne Parameterübergabe, muß sich das in ANSI C reservierte Wort »void« befinden. Zwischen den runden oder den geschweiften Klammern dürfen beliebige Leerzeichen, Tabulatoren oder Zeilentrenner stehen. Sie dürfen also so formatieren:

	main (     )  { ... }
	main() { ... }

Wir erkennen im Listing weitere Funktionen. Einige sind scheinbar nicht vorhanden, trotzdem beschwert sich der C-Compiler nicht. Des Rätsels Lösung liegt in den Bibliotheksfunktionen von C (»*.LIB«), in denen diese Funktionen definiert sind und in den Dateien »stdio.h« und »math.h«, in denen diese Funktionen deklariert, also angemeldet sind. Die Bibliotheksfunktionen von C erlauben eine sehr flexible Programmierung: je nachdem, welche Funktion Sie benötigen, bindet sie der Linker aus den Bibliotheken hinzu. Unbenutzte Funktionen hingegen bindet der Linker erst gar nicht in Ihr Programm ein. Die Funktion »main()« verlassen wir an der Stelle »return(0);«. Wie auch GFA-Basic-Funktionen können C-Funktionen Werte zurückliefern. »main()« liefert hier den Wert 0 zurück, um zu verdeutlichen, daß das Programm ohne Fehler beendet wurde.

Bibliotheksfunktionen

Das obige Beispiel verwendet die Bibliotheksfunktion »printf()«. Diese Funktion ist eine der wichtigsten C-Funktionen. Sie entspricht zwar weitgehend dem Befehl »PRINT« in Basic, hat jedoch eine andere Syntax. Während hinter dem PRINT-Befehl alle Variablen durch Kommata, Semikolons oder Hochkommata getrennt angeordnet sind, erwartet C zuerst einen Formatstring. In dieser Zeichenkette sind Variablen angemeldet (siehe Tabelle 1), die durch ein Komma abgetrennt aufgelistet stehen. Die Bibliotheksfunktion »printf()« ist eine sehr mächtige Funktion in C. Ähnlich wie »PRINT USING« bietet sie Formatierungsmöglichkeiten, mit denen sich Werte runden und tabulieren lassen. Die wichtigsten Formatierungsanweisungen für »printf()« sehen Sie in Tabelle 2.

Ausgabe-Funktionen im Vergleich

GFA-Basic C Ausgabe
PRINT "Hallo" printf ("Hallo \n); Hallo
PRINT 100 printf ("%d \n", 100); 100
PRINT 12345678 printf ("%ld \n", 12345678L); 12345678
PRINT CHR$ (7) printf ("%c \n", 7); <Glocke>

Tabelle 1. Der Unterschied zwischen »PRINT« und »printf«

Weitere Formatanweisungen entnehmen Sie dem Referenzbuch des C-Compilers. Geben Sie hinter dem »%«-Zeichen eine Zahl an, so formatiert C die Ausgabe rechtsbündig in diesem angegebenen Intervall, indem es zum Beispiel Leerzeichen voranstellt.

	printf("%1.5d \n",123);             /*Ausgabe: ‚123‘ */

Statt einer Zahl kann auch ein »*« stehen. In diesem Fall bestimmt eine zusätzliche Variable die formatierte Ausgabe.

	printf("%*d \n", 5, 123);           /*Ausgabe: ‚ 123‘ */

Folgt auf »%« ein Dezimalpunkt und dahinter eine Zahl n, rundet der Compiler die Ausgabe auf n Nachkommastellen.

	printf("%.lf \n", 12.33);	/*Ausgabe: 12.3*/

Auch in diesem Fall ist die Angabe eines »*«wie oben möglich:

	printf("%.*f \n", 3, 12.34567);	/* Ausgabe: 12.346 */

Sie haben in den Formatstrings die Zeichenfolge »\n« bemerkt. Das Zeichen »\« innerhalb eines C-Strings hat eine besondere Bedeutung: Es ist ein Steuerzeichen, mit dem Sie den Compiler anweisen, das nachfolgende Zeichen als Controlzeichen zu werten. »\n« ist also in C nur ein einzelnes Zeichen, das man sonst nur schwer in einen String schreiben könnte. Früher nannte man diese Zeichen auch Escapesequenzen. Dies führt jedoch oft zu Mißverständnissen, da z.B. für den Atari Escapesequenzen Steuerzeichen des VT 52 repräsentieren. Tabelle 3 listet die Ersatzdarstellungen auf.

Verschiedene C-Compiler, so auch Turbo C, haben Schwierigkeiten mit der formatierten Ausgabe - besonders von Fließkommazahlen. Obwohl der ANSI-Standard sehr genaue Maßstäbe liefert, erhält man immer wieder merkwürdige Ausgaben von Fließkommazahlen (Formate %f, %e, %g, %E, %G). Turbo C arbeitet insbesondere bei Ausgaben mit Rundungen fehlerhaft und unterdrückt nachfolgende Nullen nicht automatisch. Ein weiterer Fehler: Turbo C setzt offenbar bei größeren Programmen gelegentlich falsche Pointer für Formatstrings ein, was vermutlich auf falsche Berechnungen des Databereiches zurückzuführen ist.

Formatieranweisungen für »printf «

Anweisung Bedeutung Erklärung
%d dezimal 16-Bit-Integerwert mit Vorzeichen
%u unsigned 16-Bit-Integerwert ohne Vorzeichen
%x hexadezimal 16-Bit-Hexadezimalzahl
%ld long dezimal 32-Bit-Integerwert mit Vorzeichen
%lu long unsigned 32-Bit-Integerwert ohne Vorzeichen
%lx long hexadezimal 32-Bit-Hexadezimalzahl
%f float 32-Bit-Fließkommazahl
%e exponential Fließkommazahl in exp. Darstellung
%G exponential Fließkommazahl in exp. Darstellung
%c character einzelnes Zeichen
%s string Zeichenkette (mit \0 abgeschlossen)
%p pointer hexadezimale Speicheradresse
%% das Zeichen % selbst

Tabelle 2. Im Formatstring legen Sie diese Variablentypen fest

GFA-BASIC:

	' Eingaben von Daten in GFA-BASIC
	INPUT ergebnis&
	PRINT ergebnis&

C:


#include 
#include 

int ergebnisI;
main(void)
{
	*/ Eingaben von Daten in C */
	scanf("%d",&ergebnisI);
	printf("%d\n",ergebnisI);
	return(0);
}

In diesen Beispielen liest das Programm eine Integervariable von der Tastatur ein. Durch die Angabe der Variablen »ergebnis&« direkt nach dem INPUT-Befehl legt GFA-Basic eine Integervariable (16 Bit) ab. In C müssen wir dagegen die Variable erst anmelden, also noch vor deren Gebrauch zunächst definieren. Dies geschieht in der Zeile:

	int ergebnisI;  /* Definition einer 16 Bit Integervariablen in Turbo C */

Im zweiten Teil unseres Kurses gehen wir ausführlich auf die Verwendung von Variablen in C ein. Die Bibliotheksfunktion »scanf()« liest wie der entsprechende GFA-Basic Befehl »INPUT« Zeichen von der Tastatur in Variablen ein. Wie die Funktion »printf()« hat auch »scanf()« einen Formatstring, welcher der Funktion mitteilt, welche und wieviele Variablen sie zu erwarten hat. Beachten Sie das Zeichen »&« vor der Variablen »ergebnisI«. In C können wir keine Parameter an eine Funktion übergeben, die von der Funktion geändert werden. C verfügt nicht wie Pascal oder GFA-Basic über die Möglichkeit, durch »VAR«- bzw. »var«-Sprachelemente Werte am Ende einer Prozedur in die Übergabeparameter zu kopieren. Es ist lediglich erlaubt, Adressen von Variablen an die Funktion zu übergeben. Damit kann die Funktion die Werte ändern, die an den übergebenen Adressen stehen. Für die Angabe einer Adresse bietet C den Operator »&«. In unserem Beispiel übergeben wir »scanf()« die Adresse, an der die Variable »ergebnisI« steht. »scanf()« verändert so den Wert, auf den »&ergebnisI« zeigt. Zum Schluß noch Beispielprogramme zur Ein- und Ausgabe in GFA-Basic und C. (ah)


PRINT CHR$ (7) ! Zu Beginn Gebimmel.
PRINT " 1. Spalte",
PRINT "2. Spalte"
ergebnis& = 12345
PRINT "Wert von ergebnis&:"‚ ergebnis&
INPUT "Bitte ergebnis& eingeben:", ergebnis&
PRINT "geänderter Wert von ergebnis&:"‚ ergebnis&
int ergebnisI;
main(void)
{
	printf("\a"); /* Zu Beginn Gebimmel. */
	printf("1.Spalte\t");
	printf("2.Spalte\n");
	ergebnisI =12345;
	printf("Wert von ergebnis&:%d\n",ergebnisI);
	printf ("Bitte ergebnis& eingeben: scanf("%d",&ergebnisI);
	printf ("geänderter Wert von ergebnis&: %d~n",ergebnisI);
	getchar();
	return(0);
)

! Verwendung von Betriebssystemfunktionen
PRINT "Aktuelles Laufwerk :"‚ CHR$(GEMDOS (2,5) +65)
PRINT "Aktuelle Bildschirmadresse:"‚ XBIOS(2)

#include 
#include 

main(void)
{
	/* Verwendung von Betriebssystenfunktionen */
	printf ("Aktuelles Laufwerk : %c\n",(char)(gemdos(25)+65));
	printf("Aktuelle Bildschirmadresse: %ld\n", xbios(2));
	getchar();
	return(0);
}

/*Der »Cast-Operator« (char) setzt analog zu CHR$
  von GFA-BASIC die Variablenbreite auf Zeichenbreite (max. 255). */

Formatsteuerzeichen für »printf «

\a Klingelzeichen
\b Backspace
\f Seitenvorschub
\n Zeilentrenner
\r Wagenrücklauf
\t Tabulator
\v Vertikal-Tabulator
? Fragezeichen
\´ Hochkomma
" (Doppel-)Anführungszeichen
\ooo oktale Zahl (z.B. \012: dezimal 10)
\xhh hexadezimale Zahl (z.B. M: dezimal 255)
\\ das Zeichen \ selbst

Tabelle 3. Komfortabel: Mit »printf« lassen sich formatierte Ausgaben erzeugen.


Martin Hanser
Aus: TOS 05 / 1991, Seite 99

Links

Copyright-Bestimmungen: siehe Über diese Seite