14. Programmiersprache C im Vergleich zu PASCAL

Wir geben im folgenden einen kurzen Überblick über die Programmiersprache C, bei der wir die Kenntnis von PASCAL voraussetzen. Die vermittelten Kenntnisse sind für das Arbeiten mit UNIX-Systemaufrufen (system calls) notwendig. Sie dienen weiterhin dem Verständnis der in den nächsten beiden Kapiteln aufgeführten Beispiele.
C ist eine mittlere Programmiersprache: effizient wie eine Assemblersprache (vgl. in 2. Das "von Neumann'sche" Rechnermodell), portabel wie eine höhere Programmiersprache.
C kennt nur 28 reservierte Worte, wobei allein schon 13 der Typdeklaration dienen. Es gibt allerdings sehr viele Operatoren (z.B. "++", steht für "+ 1").
Ein/Ausgabeanweisungen sind nicht Bestandteil der Sprache, sondern werden als rechnerspezifische Funktionen in Form von Bibliotheken bereitgestellt. Zum Glück hat sich hierfür ein Standard herauskristallisiert.
Ein wichtiges Kriterium der Sprache C ist, daß sie nur Funktionen kennt, d.h., daß bei jedem Unterprogramm über den Namen der Routine ein Wert zurückgeliefert wird. Als Parameter kennt C ausschließlich WERT-Parameter (CALL BY VALUE), was dazu führt, daß die in einem Unterprogramm geänderten Variablenwerte über einen anderen Mechanismus dem aufrufenden Programm bekannt gemacht werden müssen. Weiterhin zeichnet sich diese Programmiersprache durch eine effiziente Implementierung von Adreß(Pointer, Zeiger)operationen aus. Dies und auch die Kürze der Schreibweise führt dazu, daß C-Programme i.d.R. unübersichtlich und schwer zu lesen sind.
Es gibt einen ANSI (American National Standard Institute)-Standard der Sprache. Hierin sind auch die Standardfunktionen aufgeführt.

Eine erste Einführung in die Sprache erfolgt anhand eines Beispiels zur Kreisberechnung.

Beispiel

Für die Radiuswerte 0, 0.5, 1, 1.5 ... 9.5, 10 sollen die Kreisflächen berechnet und die Radiuswerte, die eine Fläche größer 78 ergeben, gezählt werden.
Einführungsbeispiel in C: in der linken Spalte stehen die Anweisungen in der Sprache C. In der rechten Spalte ist ein erläuternder Kommentar aufgeführt. << TR>
Hinweis : Mit # werden Preprozessor-Anweisungen eingeleitet.
#include <stdio.h>
Standardfunktionen werden eingefügt
/* Kreisberechnung */
Kommentar
main ()
Hauptprogramm, keine Parameter
{
Programmbeginn (entspricht begin), steht vor dem Deklarationsteil
int zaehler
Deklarationen der Variablen zaehler, Typ Integer
float pi,rad,fl;
Variablen vom Typ Real
pi=3.14159;
Zuweisung, Gleitpunktzahl
zaehler=0;
 
rad=0.0;
 
while (rad <= 10.0)
Bedingungen in runden Klammern
    {
begin
    fl=pi*rad*rad;
 
    printf ("Radius = %f 
            Flaeche = %10.6f 
            \n",rad,fl);
%10.6f bedeutet 10 Stellen, davon 6 nach dem Komma
\n bedeutet neue Zeile
für die mit % gekennzeichneten Formate werden die nach dem ersten Komma folgenden Variablen eingesetzt
    rad=rad + 0.5;
 
    if (fl > 78.0) 
 
    zaehler++;
entspricht zaehler = zaehler + 1
    }
end
printf ("Anzahl Flaeche 
        >78=%d \n",zaehler);
 
}
Programmende (entspricht end.)

Der C-Compiler wird mit dem Kommando cc aufgerufen: cc datei.c Quelldateien für den C-Compiler müssen die Extension .c haben. Dies wird nicht vom Kommandointerpreter sondern vom Compiler abgeprüft.

Ein entsprechendes PASCAL-Programm für das Einführungsbeispiel hat folgendes Aussehen:

Einführungsbeispiel in PASCAL

program kreis (output);
(* Kreisberechnung *)
var zaehler: integer;
var pi,rad,fl: real;
begin
	pi := 3.14159;
	zaehler := 0;
	rad := 0.0;
	while rad <= 10.0
	do
		begin
		fl := pi * rad * rad;
		writeln('Radius = ',rad,' Flaeche= ',fl:10:6);
		rad := rad + 0.5;
		if fl > 78.0 then
		    zaehler := zaehler + 1;
		end;
	writeln('Zaehler = ',zaehler);
end.

Unsere Übersicht über die Programmiersprache C beginnt mit der Definition von Kommentaren, beschreibt die Basisobjekte und strukturierte Objekte, Variablennamen, Konstante, Operatoren und Ausdrücke. Es folgt die mit Beispielen unterlegte Beschreibung der Kontrollflußanweisungen und des allgemeinen Aufbaus von Funktionen. Anschließend gehen wir auf die Verwendung von Adressen ein und behandeln die Ein/Ausgabefunktionen. Den Schluß bildet ein Beispiel für den C-Preprozessor.

In der Programmiersprache C werden Kommentare in

	/* ... */

eingeschlossen. In PASCAL sind dies
	(* ... *).

C kennt Basisobjekte und strukturierte Objekte.

Basisobjekte
char Byte, Zeichen
int Ganzzahl
float Gleitpunktzahl

C kennt keinen speziellen Typ für Wahrheitswerte. Die Ganzzahl 0 entspricht dem Wahrheitswert "falsch", alle anderen Zahlen dem Wahrheitswert "wahr". Dies ist ein Unterschied zu den shell-Kommandos, bei denen der exit-Status 0 als korrekte Programmausführung interpretiert wird.

Strukturierte Objekte

Man unterscheidet die strukturierten Objekte. Wenn alle Elemente des Objekts vom gleichen Typ sind, spricht man von Vektoren, ansonsten von Strukturen.

Vektoren

deklaration name[zahl]

Der Index, über den die Elemente angesprochen werden, erstreckt sich von 0 bis zahl-1. Bei PASCAL wird das Indexintervall explizit angegeben und der Index beginnt in der Regel bei 1.

Beispiel:
int x[10]
Der Vektor x hat die Elemente x[0] ... x[9].

Strukturen

(vgl. C-Beispiel 4)
C PASCAL
struct name {durch ; getrennte Deklarationen} Datentyp record

Man wählt eine Komponente aus einer Struktur durch
name.Komponente

Wenn im folgenden nicht explizit der Unterschied zu PASCAL aufgeführt wird, so ist, wie bei der Selektion einer Komponente aus einer Struktur, die Syntax in beiden Programmiersprachen identisch.

Beispiel

C PASCAL
struct DATUM {
int TAG;
int MONAT;
int JAHR;
char MON_NAME[4];
}
datum = record
TAG: integer;
MONAT: integer;
JAHR: integer;
MON_NAME: array[1..4] of char
end;

Deklaration einer Struktur

C PASCAL
struct DATUM D; var D: DATUM;

Der Variablen D wird der Typ DATUM zugewiesen.

Selektion einer Komponente der Struktur

D.JAHR

Zeichenketten sind Vektoren vom Typ char. Es gibt keine speziellen Operationen für Zeichenketten, jedoch eine Funktionsbibliothek <string.h>.
Um MON_NAME (
s. Beispiel oben) einen Wert (maximal 4 Zeichen) zuzuweisen, kann die Standardfunktion strcopy (vgl. C-Beispiel 4) verwendet werden.

Variablennamen

Variablennamen können aus Buchstaben, Ziffern und dem Unterstreichungszeichen bestehen. Das erste Zeichen muß ein Buchstabe sein.

Konstanten

Zeichenketten werden in Anführungsstriche eingeschlossen. "" ist eine leere Zeichenkette, intern dargestellt durch ein Nullbyte.
Beispiele für int- und float-Konstante sind 123 bzw. 123.456.

Operatoren

Bekannt von anderen Programmiersprachen ist die Unterteilung in arithmetische, logische und Vergleichsoperatoren. Wir geben hier eine Übersicht der wichtigsten Operatoren. Hinzu kommen auch noch die weiter unten behandelten Inkrement-, Dekrement- und Adreßoperatoren. Auch die Wertzuweisung (=) ist ein Operator.

arithmetische Operatoren + - * / -(Negation) %(Modulo)
logische Operatoren && entspricht dem logischen UND
|| entspricht dem logischen ODER
! entspricht dem logischen NICHT
Vergleichsoperatoren < <= > >= == !=

Ausdrücke

Ausdrücke setzen sich aus Variablen, Konstanten und Operatoren zusammen. Klammern können verwendet werden.

Kontrollflußanweisungen

Wir behandeln die Kontrollflußanweisungen if, while (abweisende Schleife), for, do (nicht abweisende Schleife) und eine von PASCAL her nicht bekannte Anweisung (break), mittels der Schleifen verlassen werden können.

Kontrollflußanweisung if

C PASCAL
if (expression)
statement1;
[else
statement2;]
if condition then
statement1
[else statement2];

Der Ausdruck (expression) wird berechnet. Ist der Wert von 0 verschieden entspricht dies dem Wahrheitswert wahr. Man beachte, daß es in C keine expliziten Wahrheitswerte gibt. Deshalb sprechen wir auch von Ausdrücken und nicht von Bedingungen (conditions).
Statt einer Anweisung ist auch ein Block von Statements, in {} eingeschlossen, möglich.
Der else-Teil ist optional.

Kontrollflußanweisung while

C PASCAL
while (expression)
statement;
while condition
do
statement
end;

Die Schleife wird ausgeführt, wenn expression von Null verschieden ist (0 entspricht falsch).
Man beachte, daß in C eine Wertzuweisung auch ein Ausdruck ist. Der Wert dieses Ausdrucks ist der zugewiesene Wert.

Kontrollflußanweisung for

C PASCAL
for (expr1;expr2;expr3)
statement;
for index := expr1 to expr2do
statement;

for ist ein Sprachkonstrukt für eine Zählschleife. expr1, expr2 und expr3 sind Ausdrücke. expr1 dient der Initialisierung, expr2 enthält die Endebedingung und expr3 regelt die Inkrementierung/Dekrementierung (Schrittweite). expr3 gibt es in PASCAL nicht. Statt dessen kann man anstelle des Schlüsselworts to auch downto verwenden.

Kontrollflußanweisung do (nicht abweisende Schleife)

C PASCAL
do
statement
while (expression);
repeat
statement;
until condition;

Mittels
break;
kann eine Schleife verlassen werden. Eine entsprechende Anweisung gibt es in PASCAL nicht.

C-Beispiel 1

Verschiedene Möglichkeiten, die Zahlen von 1 bis 10 auszudrucken.
#include <stdio.h>
int i;
main()
	{
	i=0;
	while (i<10)
	{
	i++;
	printf("%3d",i);
	}
	printf("\n");
	do
	{
	printf("%3d",i);
	i--;
	}
	while (i>0);
		printf("\n");
	for (i=1;i<=10;i++)
	printf("%3d",i);
	printf("\n");
	i=10;
	while (1)
	{
		printf("%3d",i);
	           	i--;
	           	if (i==0) break;
	    	}
	printf("\n");
	}

PASCAL-Programm zu C-Beispiel 1

program bsp1 (output);
var i: integer;
begin
	i:=0;
	while i<10 do
		begin
		i:=succ(i);
		write(i:3)
		end;
	writeln;
	repeat
	write(i:3);
	i:=pred(i);
	until not (i>0);
	writeln;
	for i:=1 to 10 do
		write(i:3);
		writeln;
end;

Aus den arithmetischen Operatoren + und - werden durch Verdopplung Inkrement- und Dekrement-Operatoren:

Diese Operatoren können vor oder nach einer Variablen stehen. Stehen sie davor, so erfolgt die Inkrement- oder Dekrement-Operation vor Verwendung der Variablen, im anderen Falle danach.
Beispiel:
printf ("%d",i++)
Der Wert von i wird zuerst ausgegeben und dann erhöht.
printf ("%d",++i)
Der Wert von i wird erhöht und anschließend ausgegeben.

Funktionen

Als Konstrukt für Unterprogramme kennt C nur Funktionen. Prozeduren wie in PASCAL gibt es nicht. Hinzu kommt, daß C nur Parameter kennt, die als Wertparameter übergeben werden (call by value). Es gibt keine "CALL BY REFERENCE"-Parameter, die in PASCAL mittels des Schlüsselworts "var" festgelegt werden und deren Werte im Unterprogramm geändert werden können, um nach Rücksprung im aufrufenden Programm verfügbar zu sein. Da sich der Wert, der auf einer Adresse abgelegt ist, jedoch ändern kann, werden in C Adressen übergeben, um diese Funktionalität zu gewährleisten.

Aufbau einer Funktion

C PASCAL
Typ Name (Parameterliste durch , getrennt)
Parameterdeklarationen;
{
Deklarationen;
Anweisungen;
}
function name (Parameterliste inkl. Deklarationen durch ; getrennt): Typ;
Deklarationen;
begin
Anweisungen;
end;

C-Beispiel 2

Beispiel für eine rekursive Funktion: Fakultätsberechnung.
#include <stdio.h>
double fak(n)
	int n;
	{
	     if (n==0) return(1);
	     else return(n*fak(n-1));  /*der Wert wird zurückgeliefert*/
	}
main()
  {
  int n,m;
  for (;;)
  {                                                                           /*Endlosschleife */
	printf("Fakultaet von ");
	m=scanf("%d",&n);   /*scanf liest Werte ein*/
					     /*die Werte sollen außer-*/
					     /*halb der Funktion zur */
					     /*Verfügung stehen, des-*/
					     /*halb wird eine Adresse*/
					     /*übergeben (&n ist die*/
					     /*Adresse der Variablen n*/
  printf("Wert von scanf: %d\n",m);
  if (m)
	{printf("%d! = %.0f \n",n,fak(n));
	if (n==0) break;}
  else
	{printf("Fehlerhafte Eingabe\n");
	break;}
  }
}

PASCAL-Programm zu C-Beispiel 2

program bsp2 (input,output);
var n : integer;
function fak(n:integer): real;
begin
	if n=0 then fak:=1
	           else fak:=n*fak(n-1)
end;
begin
	repeat
	write('Fak. von ');
	read(n);
	writeln(n,'!=',fak(n):6:0);
	until n=0;
end.

Adressen, Zeiger, Pointer

Steht in C bei einer Deklaration vor dem Variablennamen ein "*", so handelt es sich um eine Adreß- bzw. Zeigervariable. Sie enthält Adressen von Objekten.
Beispiel:
int *px;

Die Variable px enthält als Wert eine Adresse eines Objektes vom Typ Ganzzahl. Adreßvariable gibt es auch in PASCAL:
PASCAL
type NAME = ^X;
var px: NAME;
NAME ist ein Typ, der Adressen auf ein Objekt X enthält. X kann z.B. ein "record" sein.

Für das Arbeiten mit Adressen bietet C zwei Operatoren:

In PASCAL liefert <
px^
den Wert der auf der in px gespeicherten Adresse abgelegt ist.

Die beiden Anweisungen

entsprechen
x=y.

Die Namen von Vektoren sind nichts anderes als Adressen.

Beispiel:
int a[10]
a ist die Adresse auf das erste Element des Vektors.
px=&a[0]
Der Adreßvariablen px wird die Adresse des ersten Elements zugewiesen. Die Werte von px und a sind identisch.
Demnach entspricht
a[3]
dem Ausdruck *(a+3): erhöhe die Anfangsadresse des Vektors a um 3 und gebe den darauf gespeicherten Wert aus.
Da Indexoperationen in C mit Zeigern effizienter ausgeführt werden, findet man diese Schreibweise in vielen Programmen wieder.

C-Beispiel 3

Beispiel für die Nutzung von Zeigern
#include <stdio.h>
int i,j;
 *ptr;
main()
	{
	 i=4711;
	ptr=&i; 	die Adresse der Variablen i wird der Zeigervariablen
			 ptr zugewiesen
	j=*ptr; der Wert der Variablen, deren Adresse in ptr steht,
			wird der Variablen j zugewiesen
	printf("%d %d",j,ptr);
	}

Ein/Ausgabefunktionen

C kennt keine Ein/Ausgabefunktionen. Sie werden vom Ersteller des Compilers bereitgestellt. Glücklicherweise haben sich hier Standards herauskristallisiert, die i.d.R. in der Bibliothek stdio.h zusammengefaßt sind.
Anweisungen für die formatierte Ein- und Ausgabe haben wir schon kennengelernt:

C PASCAL
formatierte Ausgabe
printf("Formatangabe",Variablenliste); writeln(Variable gefolgt von Formatanweisung);
formatierte Eingabe
scanf("Formatangabe",Liste von Zeigervariablen); readln(Variable gefolgt von Formatanweisung);

Herausgestellt werden soll nochmals, daß es sich um Funktionen handelt. Der zurückgegebene Wert liefert unter Umständen einen Fehlercode. Der erste Parameter einer Ein/Ausgabefunktion in C ist eine Formatangabe formuliert als Zeichenkette. Die nachfolgenden Parameter bezeichnen die Variablen. Da bei der Eingabe (scanf) Werte naturgemäß zurückzuliefern sind, handelt es sich bei diesen Parametern um Zeigervariablen (der Wert, nämlich die Adresse, ändert sich nicht, aber das, was auf dieser Adresse gespeichert ist). Die jeweilige Funktion nimmt die Zuordnung der Variablen zu den mit "%" eingeleiteten Formatbeschreibungen vor.

Weitere Ein/Ausgabefunktionen sind

C-Beispiel 4

Umgang mit Strukturen
#include <stdio.h>
struct person
{   char  name[50];
     float gehalt;
     int   alter;
};
struct person er;
main()
{
	strcpy(er.name,"Franz");strcpy ist eine Funktion zum
					  Kopieren von Zeichenketten;
	er.gehalt=5000.;
	er.alter=33;
	get_name(&er);
	printf("%s\n",er.name);
}
get_name(ptr)               es wird ein Zeiger auf die Struktur
struct person *ptr;         person übergeben
{
printf("Wie heißt er?");
gets((*ptr).name); printf("nochmal");
gets(ptr->name);         -> ist eine abkürzende Schreibweise
}			gets liest von der Standardeingabe eine Zeile
			fgets liest aus Dateien

C realisiert bestimmte Sprachkonzepte mit einem Preprozessor. Einen Typus von Anweisungen, #include, haben wir schon kennengelernt. Dieser bewirkt das Einfügen von Dateien. Eine andere Funktion ist der Textersatz (#define). Einen Eindruck von der Arbeitsweise des Preprozessors liefert das letzte Beispiel, das ein C-Programm fast wie ein PASCAL-Programm aussehen läßt.

C-Beispiel 5

<
Mittels des Preprozessors kann man ein C-Programm fast wie ein PASCAL-Programm aussehen lassen.
#include <stdio.h>
                               /*  Mit # werden Anweisungen für den
                                   Preprozessor eingeleitet.
                                   include      Datei wird eingefügt
                                   define       Textersatz         */
#define ZEHN 10
#define HELLO "Hello World\n"

/* #define := =  (um die PASCAL-Syntax bei Zuweisungen zu benutzen) 
                 geht leider nicht, da der zu ersetzende Name nur Buchstaben,
                 Ziffern und  _ enthalten darf.       */
/* bei der define-Anweisung dürfen auch Parameter verwendet
werden; sie werden in Klammern eingeschlossen */ #define WRITELN(i) printf("%d\n",i) #define FOR(i,von,bis,step) for(i=von;i<=bis;i+=step) #define BEGIN { #define END } #define READLN getchar() typedef int WORD; /* dies ist eine C-Anweisung: es wird ein Typname vereinbart. Als Synonym zu int kann WORD verwendet werden. */ WORD i; /* globale Variable */ main() BEGIN printf(HELLO); FOR(i,1,ZEHN,2) WRITELN(i); READLN; END