Programmieren in ANSI-C
Kapitel 3: Syntax, Präprozessor, Funktionen, Parameter, Operatoren

3.1 Die C - Syntax

Bei der Syntax handelt es sich im Wesentlichen um die "grammatikalischen" Vorschriften, die man beim Programmieren zu beachten hat. Bereits in Kapitel 2 konnte man den Beispielen entnehmen, das eine Anweisung in C immer mit einem Strichpunkt (;) abgeschlossen werden muss.

Beispiele:

if ( x <= y ) return x * y;
jahreszahl = jahrtausend * 1000 + jahrhundert * 100 +
             jahrzehnt * 10 + jahr;

Wie man sieht, ist es keinesfalls notwendig, dass eine Anweisung am Ende einer Zeile beendet sein muss. Tatsächlich können sich Anweisungen auch über mehrere Zeilen hinweg erstrecken. Wichtig ist nur, wo der Strichpunkt steht.

Um dem Compiler anzuzeigen, wo zum Beispiel eine Funktion beginnt und endet, aber auch um Schleifen und andere Kontroll-Strukturen zu begrenzen, verwendet man in C/C++ sogenannte Blockklammern ( { und } ). '{' markiert dabei den Anfang eines Blockes, '}' das Ende.


{
   // Dies ist ein Block Nr. 1
   {
      // Dieser Block ist Teil des ersten Blockes
   }
}


3.2 Der C - Präprozessor

Der C/C++ Präprozessor macht vor dem Compilieren eines Programms gewisse Vorarbeiten. So löst er zum Beispiel selbst definierte Symbole auf und bindet externe Bibliotheken ein. Im Moment möchte ich hier nur auf die beiden wichtigsten Präprozessor-Befehle eingehen:

3.2.1 #include

Die include-Anweisung sagt dem Präprozessor, welche vorgefertigten Bibliotheken er einbinden muss, damit alle im Programm verwendeten Befehle auch vom Compiler gefunden werden.

#include <stdio.h>
#include "myFunctions"

Die erste Erkentnis ist wohl, das am Ende einer Präprozessor-Anweisung kein ';' steht. Als zweites ist ersichtlich, das es offenbar zwei verschiedene Typen von include-Befehlen gibt.
  • In spitzen Klammern stehen Bibliotheken, die die Entwicklungsumgebung mitliefert, also z.B. die Standartbibliotheken stdio.h, math.h, windows.h usw. Diese liegen in einem gesonderten Verzeichnis der Entwicklungsumgebung.
  • In Hochkommas stehen Bibliothenken, die der Programmierer (also DU) selbst für dieses Programm geschrieben hat. Diese müssen im gleichen Verzeichnis liegen wie die zugehörige Programm- oder Projektdatei.

3.2.2 #define

Mit der define-Anweisung leget man sogenannte Makros fest. Der Präprozessor durchsucht den Kompletten Programmcode einer Datei und ersetzt alle Makronamen durch den Makrotext. Aufschluss soll folgendes Beispiel geben:

#define LAENGE 10
#define NEWLINE printf("\n")


/* Aus den Zeilen */
int i = LAENGE;
NEWLINE;

//Macht der Präprozessor:
int i = 10;
printf("\n");

ACHTUNG: define nimmt keinen Einfluss auf Werte, die mit einer Ziffer beginnen oder auf Text inerhalb von Hochkommas!

3.3 Funktionen

Funktionen bilden den Kern der Programmierung in den meisten Programmiersprachen. Sie bieten die Möglichkeit, bestimmte Programmteile aus dem Hauptprogramm auszugliedern. Entweder, weil sich sich öfter wiederholen und dann nur einmal geschrieben werden müssen, oder aus grüden der Übersichtlichkeit. Man unterscheidet in C nicht, wie in anderen Sprachen, zwischen Funktionen die Werte zurückgeben und Prozeduren ohne Rückgabewerte. Zu diesem Zweck gibt es in C den Typ void, der eine Funktion ohne Rückgabewert definiert.

3.3.1 Die Funktion main()

Die Funktion main bildet den Kern der Programmierung auf der DOS-Ebene. Kein Programm läuft ohne die main-Funktion. Säter werden wir sehen, das auch in der Windows-Programmierung eine Funktion Winmain existiert. Mit der Einführung von C++ hat sich die Vereinfachung breit gemacht, die Funktion als vom Typ 'void' zu deklarieren. Da jedoch in unseren Vorlesungen der in C gebräuchleiche Standarttyp 'int' verwendet wird, werde ich mich auch hier daran halten. Der Typ 'int' hat hier den Vorteil, dass das Programm einen Exit-Code an DOS zurückliefert, der z.B. in einer ERRORLEVEL-Anweisung abgefragt werden kann.


          int main ( void )

3.3.2 Andere Funktionen

Natürlich gibt es auch die Möglichkeit, andere Funktionen zu definieren. Hier ein paar Beispiele, wie so etwas in C/C++ aussieht:

double f( double x )
{
return 2*x*x + 3*x + 7;
}
void swap( int*, int* );
float Oeffne( int handle );

Jede Funktion beginnt also mit der Typdeklaration wie eine Variable auch. Darauf folgt der Name der Funktion, für den die gleichen Regeln gelten, wie für Variablen auch. In runden Klammern folgt dann eine Liste aller Übergabe- Parameter, durch Kommas getrennt.
Ich möchte hier nochmals darauf hinweisen, das bei der Namensgebung zwischen Groß- und Kleinschreibung unterschieden wird!
Definition: Wird einer Funktion -wie im Beispiel f( double x )- sofort ihr Programmcode zugewiesen, so spricht man von einer Funktions-Definition. In den hier verwendeten Programmbeispielen wird dies die Regel sein.

Deklaration: Eine Funktion darf in einem Programm erst verwendet werden, nachdem Sie dem Compiler bekannt gemacht wurde. Manchmal will man allerdings die Funktion 'main' ganz oben im Programm stehen haben oder aber die gewünschte Funktion wurde in einer anderen Bibliothek definiert. Hierzu werden die Funktionen am Anfang des Prorammes deklariert, damit der Compiler im Folgenden mit dem Funktionsnamen etwas anfangen kann. Dazu reicht ein einfacher Prototyp wie in der obigen swap-Funktion aus, den dem Compiler ist es egal, wie die Übergabe-Parameter heißen. Das dritte Beispiel zeigt eine lesbarere Form für die Deklaration. Wo genau der Unterschied ist und ob es überhaupt einen gibt, muss ich erst noch rausfinden. Natürlich muss eine Definition der Funktion trotzdem erfolgen.
Auf jeden Fall wird eine Deklaration mit einem Semikolon abgesschlossen!

3.3.3 Rückgabewerte

Aus dem ersten Beispiel oben ist bereits gut ersichtlich, was ein Rückgabewert ist. Es handelt sich um einen von der Funktion errechneten Wert, der eben von dieser Funktion zurückgegeben wird. Die Funktion 'f(x)' würde hier also für x = 2 den Wert 5 zurückgeben. Die return-Anweisung bestimmt also, welcher Wert genau zurückgegeben wird. Hier kann ein mathematischer Ausdruck, eine Konstante, ein Zeiger oder eine Variable stehen. Es ist wichtig, dass der Typ dieses Ausdrucks mit dem Typ der Funktion übereinstimmt.

3.3.4 Übergabeparameter

In runden Klammern hinter dem Funktionsnamen folgt die Parameterliste, die der Funktion übergeben wird. Jeder Parameter muss einzeln mit einem Typ versehen werden. Einzelne Parameter werden durch Kommas getrennt.
Wichtig: Die Übergebenen Variablen können von der Funktion nur funktionsintern geändert werden. Das hat keine Änderung der Parameter im Hauptprogramm zur Folge. ->Siehe Kapitel 3.4

3.4 Gültigkeitsbereiche von Variablen



double Berechnung( double parameter )
{
double interner_wert = 100.00;
double ergebnis = 0;
if ( parameter == interner_wert ) parameter = 200.01;
else parameter = interner_wert + parameter;

ergebnis = ( 5 * interner_wert ) / ( parameter - 2 * interner_wert );
return ergebnis;
}

int main ( void )
{
double interner_wert = 50.00, ergebnis, parameter = 99.00, t1, t2;
ergebnis = Berechnung ( parameter );
t1 = interner_wert;
t2 = parameter;
return 0;
}

Merke:
  1. Einer Funktion wird beim Aufruf nicht der Parameter selbst, sondern nur eine Kopie des Parameters übergeben. Dadurch wird auch der Wert des Parameters im aufrufenden Programm nicht verändert.

  2. Varaiblen sind nur dort gültig, wo sie erzeugt werden. Eine in main deklarierte Variable kann also von einer anderen Funktion nicht direkt gelesen oder verändert werden. Wie das doch geht -> Siehe Kapitel Zeiger


Mit diesen beiden Merkregeln sollten sich folgende Verständnisfragen beantworten lassen (wir betrachten nur Variablen im main- Teil des Programms, nicht in der Funktion Berechnen:
  1. Welchen Wert hat die Variable 'ergebnis' nach durchlaufen der 2. Programmzeile?
  2. Welchen Wert hat die Variable 't1' nach durchlaufen der 3. Programmzeile?
  3. Welchen Wert hat die Variable 't2' nach durchlaufen der 4. Programmzeile?
Antworten:1.)-500 2.) 50.00 3.) 99.00
Alles klar?

3.5 Operatoren

Zeit für einen kleinen Griff in die Operator-Kiste. Operatoren sind ein weiterer zentral Teil jeder Programmiersprache sind Operatoren. Man unterscheidet zwischen mathematische, logischen und Vergleichsoperatoren. Daneben gibt es noch ein paar solcher Ausdrücke, die ich nicht so recht einzuordnen weiß. Auf den Adress-Operator (&) und den Dereferenzier-Operator (*) werde ich im Kapitel über Zeiger näher eingehen.
Wie am Beispiel des Dereferenzier-Operator zu sehen ist, sind zumindest einige Operatoren Kontextabhängig, will heißen, der * kann entweder der Multiplikation oder der Dereferenzierung (was auch immer das ist) dienen. Aber sehen wir uns das mal an:

3.5.1 Mathematische Ausdrücke

Wie der Name schon sagt haben wir es hier mit dem zu tun, was die Mathematik uns an Grundrechenarten in die Hand gibt. Also Addition, Subtraktion, Multiplikation und Division. Hinzu kommt der Modulo-Operator (%), der den ganzzahligen Rest einer Divison berechnet.
Operator Beschreibung Beispiel
+ Addition x = a + b;
- Subtraktion x = a - b;
* Multiplikation x = a * b;
/ Division x = a / b;

%

Modulo - Divisonsrest x = a % b;

=

Zuweisung

x = a;


Soweit sollte das alles kein Problem sein, oder? Um sich nun ein wenig schreibarbeit zu sparen, gibt es in C die Möglichkeit, solche mathematische Ausdrücke abzukürzen. Sehen wir uns die Abkürzungen und deren Langform ebenfalls in einer Tabelle an:
Abkürzung Langform
c++; c = c + 1; siehe *
++c; c = c + 1; siehe *
c--; c = c - 1; siehe *
--c; c = c - 1; siehe *
i *= a; i = i * a;
i += b; i = i + a;
i -= a; i = i - a;
i /= a; i = i / a;
i %= a; i = i % a;

*) Der unterschied zwischen c++ und ++c (bzw. c-- und --c) besteht darin, wann die Variable um eins erhöht (bzw. erniedrigt) wird. Dieser Unterschied tritt erst zutage, wenn der Ausdruck innerhalb eines Funktionsaufrufes steht:
      x = sqrt( c++ ); heißt: ziehe die Quadratwurzel von c und erhöhe c danach um eins.
      x = sqrt( ++c); heißt: erhöhe c um eins und ziehe dann die Quadratwurzel;
Alles klar?
Achtung:Ausdrücke wie i *= a + b; heißt: i = i * (a + b); => der Ausdruck hinter dem Operator wird stehts zusammengefasst.

3.5.2 Bitweise Operatoren

Bitweise Operatoren Verknüpfen zwei Variablen Bit für Bit miteinander. Neben den boolschen Operatoren UND, ODER und Exclusiv- Oder (XOR) gibt es noch sogenannte Bitschiebebefehle. Diese verschieben einen binären Wert (jeder Speicherinhalt ist ein binärer Wert) um ein Bit in die entsprechende Richtung, wobei auf der anderen Seite jeweils eine null eingeschoben wird. Dabei kann auch angegeben werden, um wieviele Bit verschoben werden soll. Aber sehen wir uns zunächst an, was passiert, wenn man zwei Bits miteinander UND, ODER oder XOR miteinander verknüpft. Die eingehenden Bits stehen unter a und b, in den anderen Spalten stehen dann jeweils die Ergebnis-Bits.

a

b

UND

ODER

XOR

0

0

0

0

0

0

1

0

1

1

1

0

0

1

1

1

1

1

1

0


Nun können wir uns auch gedanken darüber machen, wie man diese Operatoren einsetzt:
Ausdruck Bedeutung Beispiel Abkürzung
& UND x = x & b; x &= b;
| ODER x = x | b; x |= b;
^ XOR x = x^ b; x ^= b;
<< Bitshift nach links x = x << n; x <<= n;
>> Bitschift nach rechts x = x >> n; x >>= n;
Achtung: Die boolschen Operatoren können nicht dazu verwendet werden, zwei Variablen zu vergleichen. Dazu müssen die Vergleichsoperatoren herangezogen werden!

3.5.3 Vergleichsoperatoren

Am Ende schließlich soll auf die Vergleichsoperatoren in C/C++ eingegangen werden. Wie zu erwarten lassen sich damit zwei Variablen oder Werte miteinander vergleichen. Auf die bekannten mathematischen vergleiche möchte ich gar nicht näher eingehen. Die Verwendung von <, >, <= und >= ist analog zur Algebra.
Achtung: Der Test auf Gleichheit zweier Werte geht mit '==' und nicht mit dem einfachen Gleichheitszeichen. Dieser Umstand darf auf keinen Fall vergessen werden.
Hier nun eine Auflistung der Vergleichsoperatoren in gewohnter Form:
Operator Beschreibung Beispiel
== Gleichheit if ( a == b )
!= Ungleich if ( a != b )
&& UND if ( ( a != b ) && (c == d) )
|| ODER if ( ((a || b) == f) && (c != b))
! Negation if ( !((a || b == c ) && (c != d)))


© Gerhard Zapf
Datentypen
Sitemap
www.tutorialpage.de
Ein-/Ausgabe
Diese Seite wurde seit dem 12.07.01 genau 7922 mal abgerufen.
Letzte Änderung: 11.09.2003