Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
LiFePO4 Speicher Test

Ein kurzer Einblick in die Programmiersprache C

Allgemeines

C wurde 1971 als Gundlage für das Betriebssystem UNIX in den USA entwickelt (UNIX ist zu über 90% in C geschrieben). 1978 wurde von Brian Kernighan und Dennis Ritchie eine eindeutige Sprachedefinition entwickelt. C ist mittlerweile von ANSI und ISO standardisiert. Heutzutage ist C (und ihr Nachfolger C++) die dominierende Programmiersprache. Sehr viele Anwendungen sind in C geschrieben. Leider ist C jedoch nicht einfach zu lernen, daher eignet es sich nur bedingt für Anfänger. Mit etwas Übung kann man damit jedoch sehr effiziente Programme schreiben.

C ist sehr eng an Assembler angelehnt, aber trotzdem Hardware-unabhängig. Das bedeutet, Sie können maschinennahe Programme sehr leicht (aber nicht ganz ohne Aufwand) auf ein anderes System portieren. Sie benötigen dazu lediglich einen anderen Compiler, und Inline-Assembler Anweisungen (Assembleranweisungen innerhalb eines C-Programmes) müssen der neuen Hardware (Prozessor) angepasst werden.

Geschichte

1971
C wird entwickelt
1978
Kernighan und Ritchie definieren die Sprache.
1983
ANSI und ISO standardisieren C.
1992
Bjarne Stroustrup enwickelt die Nachfolgesprache C++.

Aufbau eines C-Programmes

C-Programme haben keinen fixen Aufbau wie z.B. Pascal. Es gibt zwar gewisse Regeln, aber sonst sind dem Programmierer alle Freiheiten überlassen. Der folgende "Beispiel-Aufbau" ist daher nicht zwingend und kann durchaus verändert werden.

#include <conio.h> // Header-Include
#include <stdio.h> // 

int Zahl1;
char Zeichen1;

int main(void)
{
   int Zahl2;
   //Anweisungen
   return 0;
}

Die Beschreibung (die Erklärungen folgen):

  • Die beiden #include <...> Anweisungen sagen dem Compiler, welche Header-Dateien er einbinden soll. In den Header-Dateien und den dazugehörigen Bibliotheken stehen Anweisungen und Datentypen, die nicht im Compiler implementiert sind. Dazu ein Beispiel: Mathematische Anweisungen wie +,-,*, etc. sind, weil oft benötigt, im Compiler "eingebaut". Ein- und Ausgabefunktionen jedoch, die verhältnismäßig selten benötigt werden, stehen in der Datei "stdio.h".
  • Die Anweisung "int Zahl1;" definiert eine Variable vom Typ int (integer, ganze Zahl zwischen −32.768 und +32.767). Diese Variable ist im ganzen Programm gültig (global).
  • Das selbe geschieht bei "char Zeichen1;", nur wird diesmal eine Variable des Types char (character, ASCII-Zeichen oder Zahl zwischen -127 und +128).
  • "void main(void)" definiert ein Unterprogramm mit dem Namen main, das keine Parameter hat (void) und eine ganze Zahl (int) zurückliefert. "main" ist aber nicht irgendein Unterprogramm, sondern es wird beim Programmstart aufgerüfen. Es entspricht daher dem "Hauptprogramm" von Pascal.
  • Die geschwungenen Klammern beginnen bzw. beenden einen Block, in diesem Fall den von "main".

Innerhalb von "main" wird noch eine Variable definiert, Zahl2.

  • Danach folgen die Anweisungen.
  • "return", gefolgt von einem Wert, beendet das Programm.

Das Unterprogramm MAIN

Unterprogramme lernen Sie zwar erst später kennen, aber dieses eine muß sein. In Gegensatz zu anderen Programmiersprachen gibt es in C kein "Hauptprogramm", sondern nur Unterprogramme. Aber woher soll der Computer wissen, welches Unterprogramm er am Beginn ausführen soll? Ganz einfach, er führt immer das Unterprogramm "main" aus. Dieses ist sozusagen das "Hauptprogramm". Der Mainfunktion können bei PC Parameter übergeben werden. Dies sind die sogenannten Kommandozeilenparameter die beim Aufruf eines Programmes hinter dem Dateinamen stehen. Zudem wird auch ein Integer Wert als Ergebnis zurückgeliefert. Beim Microcontroller ist main ja gewöhnlich das Startprogramm das nach dem RESET aufgerufen wird, hier gibt es dann weder Funktionsparameter noch Rückgabewert, daher wird beides oft auch als VOID (Parameterfrei) definiert, wie oben im Beispiel zu erkennen.

void main(void)  //void Definition nur beim Controller üblich
{
}

Was das bedeutet, lernen Sie in einem späteren Kapitel. Wichtig ist nur - ohne main ist kein Programm komplett, es lässt sich auch nicht compilieren und ausführen!

Blöcke

Im vorigen Abschnitt haben Sie bereits die geschwungenen Klammern { und } kennen gelernt. Doch was bedeuten Sie? Einem Pascal-Kenner ist das schnell erklärt: { entspricht BEGIN, } entspricht END. Wenn ihnen auch das unbekannt ist, dann hilft Ihnen hoffentlich die folgende Erklärung. Programme sind in Abschnitte unterteilt. Da gibt es zum einen das Hauptprogramm und die jeweiligen Unterprogramme, aber auch Schleifen und bedingte Anweisungen. Jedes dieser Beispiele stellt ein eigenständiges Stück Code dar. Daher müssen Sie es auch als solches kennzeichnen. Dies geschieht mit { und }. { bedeutet so viel wie "Block Anfang" und } bedeutet "Block Ende":

void main(void)
{    //der Block "main" beginnt
 int Zahl;
 
 {   //ein "namenloser" Block beginnt
   //hier stehen Anweisungen
 }   //der "namenlose" Block endet
}    //der Block "main" endet

Bemerkung: Der "namenlose" Block ist eigentlich unnötig. In den folgenden Kapiteln lernen Sie jedoch bessere Beispiele kennen!


Variablen und Datentypen

Variablen

Eine Variable ist ein Synonym (=anderer Name) für eine Speicherstelle in einem Computer. Einfacher gesagt, eine Variable bietet Raum, um Daten (z.B. eine Zahl) zu speichern und wieder zu lesen. Dieser "Raum" wird im Schreib- u. Lesespeicher (RAM) des Computers angelegt.

Datentypen

Der Datentyp einer Variable gibt an, welche Werte eine Variable enthalten kann, und welcher Art diese Daten sind. So ist es zum Beispiel möglich, in eine Variable vom Typ int eine Zahlen zwischen ca. -32000 und +32000 einzutragen. In einer Char-Variable können Sie alle möglichen ASCII-Zeichen speichern (alles, was Sie mit der Tastatur erzeugen können).

ACHTUNG: Da C plattformunabhängig ist, hängt die Größe eines Datentypes zum Teil von der genutzten Hardware (z.B. 8, 16 oder 32 Bit Controller) und dem Compiler ab!

int, word, double (ganze Zahlen)

In Variable dieses Types können Sie ganze Zahlen abspeichern, also z.B. 1, 2, 100, 12345.

Größe (Bit) Namen, Javaname Vorzeichen Grenzen des Wertebereichs (Zweierkomplement)
8 Byte signed
unsigned
−128
0
127
255
16 Word, Short signed
unsigned
−32.768
0
32.767
65.535
32 Double Word, Integer signed
unsigned
−2.147.483.648
0
2.147.483.647
4.294.967.295
64 Quad Word, Long signed
unsigned
−9.223.372.036.854.775.808
0
9.223.372.036.854.775.807
18.446.744.073.709.551.615



float, double Gleitkommazahlen

In einer Gleitkomma-Variable können Kommazahlen gespeichert werden, z.B. 3.141592654. Float reicht reicht für die meisten Kommazahlen. Werden jedoch noch höhere Genauigkeiten benötigt, kommt der Datentyp double zum Einsatz. Vorsicht: bei PIC (microchip) ist die innere Darstellung dieser Zahlen anders als bei meisten anderen Compilern, beim binären senden z.B. zum PC muß dann konvertiert werden !

char (Zeichen)

In einer vorzeichenbehafteten char-Variable können Sie Zahlen zwischen -128 und +127 abspeichern oder 0 - 255 ohne Vorzeichen. Dieser Datentyp wird oft für ASCII-Zeichen genutzt (für den Computer ist es ja egal, ob sich eine Zahl oder ein Zeichen in der Variable "befindet", er speichert "alles" in Form von binären Ziffernfolgen). Dabei darf man eines nicht vergessen: Es macht einen großen Unterschied, ob man in einer char Variablen das Zeichen "1" (ASCII-Zeichen Nr. 49) abspeichert, oder die Zahl 1 (entspricht Hardware-technisch ASCII-Zeichen Nr. 1). Man kann zwar mit beiden rechnen, aber "1" * 2 ergibt nicht "2", sondern "b" (ASCII-Zeichen Nr. 98)!

Strings (Zeichenketten)

Bei Strings handelt es sich um Wörter und Sätze, die aus einzelnen Zeichen gebildet werden. Jede Ausgabe auf dem Bildschirm funktioniert mittels Strings. Wie dieser Datentyp genau funktioniert, erfahren Sie im Kapitel besondere Datentypen.

Boolean (Logische Variablen)

Dieser Typ kann nur zwei Werte annehmen: "TRUE" und "FALSE". Dabei handelt es sich um logische (boolsche) Werte (deutsch: "wahr" oder "falsch"). Man kann damit logische Vergleiche machen (siehe Kaptitel 3). C hat dafür keinen eigenen Datentyp, sondern es können int und char dafür verwendet werden. Hat die jeweilige Variable den Wert 0, so ist sie FALSE, sonst (ungleich 0) ist sie TRUE. HINWEIS: In Zukunft werden ich oft 1 als Synonym für "ungleich 0" verwenden. Bitte beachten Sie jedoch, das eine Variable, die TRUE ist, nicht unbedingt den Wert 1 haben muß. Sie muß lediglich ungleich 0 sein!

Anlegen von Variablen

Um eine Variable verwenden zu können, müssen Sie sie zuerst vereinbaren ("erzeugen", "bekannt machen"). Dies geht ganz einfach: Schreiben Sie zuerst den Datentyp, dann den Namen der Variablen. Zum Schluß kommt noch der Strichpunkt. Und nicht vergessen, C ist "case-sensitive", d.h. der Datentyp muß klein geschrieben werden.

int Zahl1, Zahl2;
char Zeichen;

void main(void)
{
  float gleitZahl1;
  // Anweisungen
}

Erklärung: In einer Zeile können auch mehrere Variablen gleichen Types vereinbart werden, wenn man einen Beistrich dazwischen setzt. Variablen können in jedem "Block" vereinbart werden. Siehe Gültigkeitsbereiche.

Zuweisungen

Im man kann einer vereinbarten Variable nun Werte zuweisen. Dazu schreibt man zuerst den Variablennamen, ein Gleichheitszeichen "=" und anschließend den zuzuweisenden Ausdruck.

void main(void)
{
  int Zahl1, Zahl2=12;
  char Zeichen1="A".

  Zahl1=52;
  Zeichen1=Zeichen1+1; 
}

Was macht das Program:

Zuerst werden 3 Variablen angelegt (Zahl1, Zahl2, Zeichen1). Zahl2 wird gleich bei der Vereinbarung der Wert 12 zugewiesen (Ja, auch das ist möglich). "Zahl1=52;" - Hier wird der Variablen Zahl1 der Wert 52 zugewiesen. Ist doch ganz einfach! Zeichen1 wir um 1 erhöht. Da in der Variablen "A" gespeichert ist, gibt es ein keines Problem. Was ist "A" + 1 ? Aber wer brav aufgepasst hat, weiß das schon. "A" entspricht der Zahl 65, und 65 + 1 ist 66. Nun ist in der Variablen Ziffer1 der Wert 66 abgespeichert, was wiederum dem ASCII-Zeichen Nr. 66 entspricht, welches "B" ist. Verstanden?

Zuweisungen bei float

Das funktioniert genau wie normale Zuweisungen; Nachkommastellen werden durch einen Punkt abgegrenzt. ..... floatVariable=3.14; ....

Zuweisungen bei logischen Variablen

Wie bereits erwähnt, besitzt C keinen logischen Datentyp. Es müssen also int und char dafür genutzt werden. Die Zuweisung entpricht der Standard-Zuweisung. Wird der Wert 0 zugewiesen, dann ist die Variable FALSE, ansonsten ist Sie TRUE. ..... intVariable:=123; //entspricht in Pascal: boolVariable:=true; intVariable:=1; //entspricht in Pascal: boolVariable:=true; intVariable:=0; //entspricht in Pascal: boolVariable:=false; .....

Vergleiche von Variablen

Sie können Variablen miteinander vergleichen. Das geschieht mit einem der folgenden Zeichen mit den normalen mathematischen Regeln: == ("ist gleich"), != ("ist nicht gleich"), < ("ist kleiner"), <= ("ist kleiner oder gleich"), > ("ist größer"), >= ("ist größer oder gleich"). Als Ergebnis bekommen Sie einen logischen Ausdruck in Form einer int-Zahl. ..... intVariable= Zahl1<=Zahl2; ..... intVariable ist ungleich 0 (=TRUE), wenn Zahl1 kleiner oder gleich Zahl2 ist, ist Zahl1 jedoch größer als Zahl2, dann ist intVariable 0 (FALSE).


Konstanten

Konstanten können als Variable angesehen werden, die nicht beschrieben, sondern nur gelesen werden können. Ein typischen Beispiel dafür ist die Zahl PI (rund 3,141592654). Niemand würde in der realen Welt versuchen, ihr einen anderen Wert zuzuweisen. Würde man PI jedoch wie eine normale Variable anlegen, wäre dies ohne weiteres möglich. Um dies zu verhindern, gibt es die Konstaten:

void main(void)
{
  const pi=3.141592
}

Wichtig dabei ist, dass man Konstanten nur bei der Vereinbarung einen Wert zuweisen kann (Aber glauben Sie nicht ewig daran, es gibt Wege, dies zu umgehen!).

Gültigkeitsbereiche

Jede Variable ist nur in dem Block gültig, in dem Sie vereinbart wurde, und in allen darunterliegenden. Globale Variablen (solche, die außerhalb eines Blockes vereinbart wurden, also vor main) sind im gesamten Programm gültig. In C können mehrere Variablen den gleichen Namen haben, solange eindeutig ist, welche in welchen Block gültig ist. Dabei gelten folgende Regeln:

Globale Variablen gelten überall

Wird jedoch in einem Unterprogramm wie z.B. main eine Variable gleichen Namens angelegt, gilt ab hier bis zum Ende des Blockes (main) nicht mehr die globale Variable, sondern die von main. Das Spiel kann man weiterspielen: wird in einem Unter-Block von Main wieder eine "gleiche" Variable angelegt, gilt diese, und nicht die von main (und schon garnicht die globale :-) ).

Kontrollanweisungen

Boolsche Logik

Boolean-Variablen können miteinander verknüpft werden. Dies geschieht mit den boolschen Operatoren && ( logisches "und"), || (logisches "oder") und ! (logisches "nicht") (es gibt noch weitere, auf die hier nicht eingegangen wird).

b1= b2 && b3;


Der Wert von b1 ist dann 1 (TRUE), wenn b2 und ("AND") b3 1 (TRUE) sind. Ist eine der beiden (oder auch beide) 0 (FALSE), dann ist b1 ebenfalls 0 (FALSE).


b1= b2 || b3;


Der Wert von b1 ist dann TRUE, wenn b2 oder ("OR") b3 TRUE ist. Ist also einer (oder beide) TRUE, dann ist auch b1 TRUE, sind beide FALSE, ist auch b1 FALSE.


b1= !b2;


Der Wert von b1 ist dann TRUE, wenn der von b2 FALSE ist. Die NOT Verknüpfung "dreht den logischen Wert der Variablen um".

IF - Anweisung

Syntax: IF (Bedingung) Anweisung; Mit Hilfe der IF Anweisung kann man Codeteile (Blöcke) ausführen lassen, wenn die dazugehörige Bedingung erfüllt (TRUE) ist.

if (Zahl1 == 0) Zahl2=10;

Die Anweisung Zahl2=10 wird nur ausgeführt, wenn Zahl1==0 TRUE ist. Will man in die IF Abfrage mehrere Anweisungen einschließen, benötigt man eine Block mit { und }. Die Klammer um die Bedingung ist - Bedingung!

if (Zahl1 >= 0) then
{
       Zahl2=Zahl2+1;
       Zahl1=Zahl2;
}

Schleifen

Um Anweisungen mehrmals hintereinander ausführen, benötigt man Scheifen. Diese führen Anweisunge aus, bis oder solange Bedingungen erfüllt sind.

WHILE-Schleife

Syntax: while (Bedignung) Anweisung; Die WHILE-Schleife wird solange durchlaufen, bis die Bedingung FALSE ist (deutsch: "WÄHREND Bedingung TUE Anweisungen"). Die Schleife wird mindestens Null mal durchlaufen. Bei mehreren Anweisungen benötigen Sie einen Block!

Zahl1=0;
while (Zahl1<3)
{
   Zahl1=Zahl1+1;
   Zahl2=Zahl2*2;
}

In diesem Beispiel wird die Schleife 3 mal durchlaufen. Zu Beginn des 4. Durchlaufes ist die Bedingung FALSE (Zahl1 ist dann nicht mehr kleiner, sondern gleich 3!), also wird mit dem Befehl nach der Schleife fortgesetzt.

FOR-SCHLEIFE

Syntax: for (Zuweisung;Bedingung;Inkrement) Anwseisung; Die FOR-Schleife ist eine Zählschleife. Die angegebene Variable wird am Anfang auf einen Wert gesetzt. Danach wird sie nach jedem Durchlauf verändert (meist um 1 erhöht). Hat sie einen bestimmten Wert erreicht, wird die Schleife beendet. Innerhalb des Anweisungsblockes können Sie auf die Variable zugreifen, d.h. Sie können sie auch verändern. Das ist jedoch nicht ratsam. Wollen Sie mehrere Anweisungen ausführen, benötigen Sie eine Block

Die obenstehende Syntax wird Pascal- oder Basic-Kenner verwundern. Aber das "neue" System ist schnell erklärt: Bei "Zuweisung" wird der Zählvariable ein Wert zugewiesen (entspricht Anfangswert bei Pascal). Die gesammte Schleife wird solange durchgeführt, bis Bedingung FALSE ist (d.h. Sie brauchen garnicht die Zählvariable abfragen, Sie können hier auch jede andere Variable auswerten). Inkrement letztendlich wird nach jedem Schleifendurchlauf ausgeführt. Hier kann man z.B. den Zähler erhöhen.

Die Theorie ist schwer, die Praxis nicht so sehr...

int Zaeler;
for (Zaeler=1; Zaeler<=10; Zaeler++) 
{
   Zahl2=Zahl2+Zaeler;
}

In diesem Beispiel wird Zahl2 in jedem Schleifendurchlauf um die Zählvariable Zaeler erhöht. Da Zaeler nacheinander die Werte von 1 bis 10 hat, ist in Zahl2 nach der Schleife die Summe der Zahlen 1 bis 10 gespeichert.

Erklärung: Zaeler=1 bedeutet, dass der Variablen Zaeler vor dem 1. Schleifendurchlauf der Wert 1 zugewiesen wird. Zaeler<=10 ist die Abbruchbedingung; ist sie FALSE, wird die Schleife beendet. Zaeler++ bedeutet, dass Zaeler nach jedem Durchlauf um 1 erhöht wird.


Ein- und Ausgabe

Bildschirm-Ausgabe

Bisher war das Tutorial, trotz aller Beispiele, reine Theorie. Sie konnten zwar Programme schreiben, aber die Funktion nicht testen. Damit ist jetzt Schluß. Hier lernen Sie, wie Sie etwas am Bildschirm ausgeben. Der dazu notwendige Befehl ist printf (das f ist KEIN Fehler!). Diese Anweisung gibt die ihr übergebenen Parameter auf dem Bildschirm aus (außer, Sie haben unter DOS die Standard-Ausgabe auf ein anderes Gerät, z.B. den Drucker umgeleitet, aber das ist eine andere Geschichte ...). Sie kann beliebig viele Parameter übernehmen. Es müssen jedoch Standard-Datentypen (z.B. int, char, float, double) sein!

#include <conio.h>
#include <stdio.h>

void main(void)
{
    int Zahl1=12;
    char Zeichen1="A";
    
    printf("Das ist Text, und er wird als solcher ausgegeben. \n");
    printf("Der Wert der Variablen Zahl1 ist: %d \n",Zahl1);
    printf("Der Wert der Variablen Zeichen1 ist: %c \n",Zeichen1);
    printf("Der Wert der Variablen Zeichen1 ist: %d \n",Zeichen1);
}


Hinweis: Dass das Programm nach dem Compilieren und Ausführen sofort wieder beendet wird, ist nicht Ihr Fehler. Drücken Sie [ALT] + [F5], um zum Ausgabebildschirm zu kommen! Erklärung:

Der 1. printf Befehl gibt Text aus. Das Zeichen am Ende (\n) bedeutet "New Line", es bewegt den Cursor an den Anfang der nächsten Zeile (In Pascal verwendet man dafür bekanntlich Writeln). Der 2. printf-Befehl gibt auch Text aus, am Ende befindet sich wieder das \n, um einen Zeilenvorschub zu erreichen. Aber was bedeutet %d ? Ganz einfach, der Compiler ersetzt %d durch den 1. Parameter, der (hoffentlich!!!) nach dem Text angegeben wird. In diesem Fall wird %d durch den Wert der Variablen Zahl1 ersetzt. Das d im %d bedeutet "Dezimalzahl", der Computer gibt also eine Zahl aus. In der 3. Ausgabe wird ein Zeichen ausgegeben. Diesmal bedeutet %c "char" (Zeichen). Es wird also %c durch "A" ersetzt. Die letzte Ausgabe verhält sich merkwürdig - es wird doch tatsächlich eine Zahl ausgegeben, obwohl der Parameter ein Zeichen (Char) ist. ABER: %d im Text bedeutet, dass eine Zahl auszugeben ist. ein Char kann ja auch als Zahl interpretiert werden. Es wird also 65 (der ASCII-Wert von "A") ausgegeben. Das ist ein typisches Beispiel für das mögliche unterschiedliche Interpretieren einer char Variablen! Bildschirm löschen Oft sind von anderen Programmen noch "Rückstände" vorhanden. Nicht nur um die zu beseitigen, brauchen Sie einen "Bildschirm-Lösch-Befehl". Dieser ist in conio.h definiert und nennt sich "clrscr" ("clear screen", deut: "lösche Bildschirm").

#include <conio.h>
#include <stdio.h>

void main(void)
{
  clrscr();
  ....
}


Tastatur-Eingabe

Um ein "gscheites" Programm schreiben zu können, muß man wissen, wie der Benutzer über die Tastatur Befehle eingeben kann. Die dafür notwendigen Funktionen stelle ich in diesem Kapitel vor. Der wichtigste Befehl ist "scanf". Er liest Daten von der Tastatur. Die Syntax entspricht derer von printf, nur daß man außer den %c und %d keine weiteren Angaben im "Text" machen darf:

printf("Bitte geben Sie eine Zahl ein: ");
scanf("%d",&Zahl1);
printf("Geben Sie einen Zeichen ein: ");
scanf("%c",&Zeichen1);

Das Programm gibt eine Eingabeaufforderung aus. Dann erwartet es vom Benutzer, daß er eine Zahl eingibt, die mit [ENTER] bestätigt wird. Diese Zahl wird in Zahl1 abgespeichert. Danach erfolgt wiederum eine Aufforderung zur Eingabe, diesmal eines einzelnen Zeichens. Dieses kann man nun eingeben und ebenfalls mit [ENTER] bestätigen.

Macht man keine dem Datentyp der erwarteten Variable entsprechende Eingabe, dann bricht das Programm mit einer Fehlermeldung ab (wenn man z.B. "1_T2" eingibt, wenn eine Zahl erwartet wird)!

Das & vor den Parametern ist notwendig. Warum, das erfahren Sie im Kapitel "Unterprogramme". Für die Profis eine Kurz-Erklärung: Das Unterprogramm scanf bekommt zwar einen Wert übergeben, kann aber keinen zurückliefern ("call by value"). Daher wird kein Wert, sondern ein Zeiger auf eine Variable übergeben. Mit dem & Zeichen bekommen Sie die Adresse einer Variablen ("call by reference").

getch

getch ist eine Funktion (Siehe Kapitel Unterprogramme), die ein char-Zeichen von der Tastatur einliest. Dazu geben Sie im Programm einfach folgede Zeile an:

charVariable=getch();

Diese Zeile erwartet die Eingabe eines CHAR-Zeichens. Im Gegensatz zu scanf muß man nach der Eingabe des Zeichens jedoch nicht [ENTER] drücken, um die Eingabe zu bestätigen. In der Variable charVariable wird das erste Zeichen gespeichert, das auf der Tastatur eingegeben wird. Eine weiter Möglichkeit der Anwendung von getch ist es, wenn Sie einfach "getch();" als Befehl eingeben. Dann wartet der Computer, bis Sie eine Taste drücken, speichert diese aber nicht ab.

kbhit

Dies ist ebenfalls eine Funktion, die eine boolschen Ausdruck zurück liefert. Sie gibt Bescheid darüber, ob eine Taste gedrückt wurde, und mit scanf oder getch abgerufen werden kann (zum Verständnis: auf der Tastatur eingegebene Zeichen werden meist nicht direkt vom Programm verarbeitet, sondern erst vom Betriebssystem bzw. dem BIOS des Computers gespeichert. Wenn das Programm nun eine Eingabe erwartet, werden die gespeicherten Zeichen "verwendet". Dies lauft jedoch so schnell ab, daß der Benutzer davon nichts merkt). Ist diese Variable TRUE, dann ist ein Zeichen abfragebereit, sonst nicht:

while ( !kbhit() )
{
  Zahl1=Zahl1+1;
  printf("%d\n",Zahl1);
}

Dieser Programmteil erhöht solange Zahl1 und gibt diese aus, bis der Benutzer eine Taste drückt.


Allgemeines zu Unterprogrammen

Stellen Sie sich vor, Sie haben einen eine Code-Folge, die mehrmals im Programm vorkommt, z.B. eine mathematische Formel. Anstatt dieses Codestück mehrmals zu schreiben (was Zeit beim Erstellen und Speicherplatz im ausführbaren Programm kostet), können Sie den Abschnitt in ein sogenanntes "Unterprogramm" schreiben. Dieses Unterprogramm können Sie dann von jeder Stelle ihre Hauptprogrammes aus aufrufen. Anders als in anderen Programmiersprachen gibt es in C keine generelle Unterscheidung zwischen Funktionen und Prozeduren. Trotzdem möchte ich kurz darauf eingehen:

Funktionen

Syntax: Rückgabe-Typ Name(Parameterliste);

Funktionen sind Unterprogramme, die am Ende der Ausführung einen Wert zurückliefern, wie z.B. getch. Paradebeispiele für die Anwendung einer Funktion sind jedoch mathematische Formeln:

#include <conio.h>
#include <stdio.h>

int hoch2(int param1)
{
  int zahl;
  zahl=Param1*Param1;
  return zahl;
}

void main(void)
{
  int zahl1,ergebnis;
  
  printf("Bitte Zahl eingeben: ");
  scanf("%d",&zahl1);
  ergebnis:=hoch2(zahl1);
  printf("%d hoch 2 = %d\n",zahl1,ergebnis);
  printf("5 hoch 2 = %d\n",hoch2(5));
  getch();
}

Bemerkungen zum Programm: Ein Unterprogramm kann an jeder beliebigen Stelle innerhalb eines Programmes stehen, aber nur außerhalb von Blöcken. Siehe auch Prototypen. Geschachtelte-Unterprogramme wie z.B. in Pascal sind nicht möglich. In der Deklaration der Funktion kommt zuerst der Rückgabe-Datentyp. Jede Funktion liefert einen Wert zurück, dieser Datentyp gibt den Typ der Varaiblen an. Bei z.B. getch ist das char. Danach folgt der Name der Funktion (case-sensitiv!). Danach kommt die Parameter-Liste. Dabei handelt es sich um eine Liste von Parametern, die jeweils durch Typ und Name angegeben werden, und eventuell mittels Beistrich voneinander getrennt werden. Am Ende ist kein Beistrich! Im Block der Funktion können alle Anweisungen ausgeführt werden und Variablen deklariert werden. Am Ende der Funktion muß diese ordnungsgemäß beendet werden. Dies geschieht mittels der Anweisung "return" und dem Rückgabewert. Um eine Funktion aufzurufen (aus einem anderen Unterprogramm oder main), geben Sie folgendes an "Variable=Funktion(Parameter);". Bei Variable muß es sich jedoch nicht unbedingt um eine von Ihnen deklarierte Variable handeln. Es kann z.B. auch der Parameter für ein weiteres Unterprogramm sein (wie im Beispiel, letzte Zeile). Die Einrückungen der Anweisungen innerhalb der Funktion nach rechts ist nicht notwendig, sie dient jedoch der Übersicht und ist sehr empfehlenswert.

Prozedur

Syntax: void Name(Parameterliste);

Eine Prozedur ist nichts anderes als eine Funktion ohne Rückgabewert. Sie ist daher prinzipiell gleich aufgebaut wie eine Funktion, bis auf die folgenden Unterschiede:

Als Rückgabe-Datentyp wird void angegeben. Eine Prozedur kann daher beim Aufruf nicht einer Variablen zugewiesen werden (wie FUNCTION's) sonder wird wie eine normale Anweisung behandelt (wie z.B. printf).

#include <conio.h>
#include <stdio.h>

void ausgeben(int Param1);
{
  printf("Der Wert der Variablen beträgt: %d\n",Param1);
}

void main(void)
{
   int Zahl1;
   ausgeben(23);
   Zahl1=4;
   ausgeben(Zahl1);
   getch();
}

Bemerkungen zum Programm: Durch den Befehl "ausgeben(23)" wird die Prozedur "ausgeben" mit dem Parameter 23 aufgerufen. Param1 hat also den Wert 23. Die einzige Anweisung innerhalb der Prozedur ist printf, was nun ausgeführt wird. Danach wird mit dem nächsten Befehl innerhalb des Hauptprogrammes fortgefahren ("Zahl1=4"). Der 2. Aufruf von ausgeben erfolgt mit dem Parameter Zahl1, also wird Param1 der Wert 4 zugewiesen. Dieser wird nun wieder mittels printf ausgeben. Unterprogramme ohne Parameter Das ist ihnen sicherlich schon aufgefallen: Viele Unterprogramme, z.B. main, haben statt der Parameter immer nur void stehen. Was bedeutet das? Nun ja, C erwartet, das Sie die Parameter angeben. void bedeutet "es gibt keine Parameter". "void main(void)" bedeutet also: "Erstelle ein Unterprogramm Namens main, das keinen Rückgabewert und keine Parameter hat". ACHTUNG! Auch wenn eine Funktion keine Parameter hat, z.B. getch, müssen Sie beim Aufruf trotzdem die Klammern angeben. Der Befehl "getch;" führt nicht getch ohne Parameter aus, sondern liefert die Adresse der Funktion getch. Alles klar? Na ja, jedenfalls: Beim Aufruf jedes Unterpgrogrammes immmer Klammern angeben.

Prototypen

Wie oben erwähnt, kann ein Unterprogramm an jeder beliebigen Stelle im Programm stehen. Damit ist jedoch eine Bedingung verknüpft: Das Unterprogramm muß in der Datei oberhalb des 1. Aufrufes definiert worden sein. Wenn Sie ein Unterprogramm in Zeile 10 zum ersten mal aufrufen, müssen Sie die Definition davor erledigt haben. Verstanden? Um dies zu erreichen, gibt es zwei Möglichkeiten:

Entweder Sie schreiben alle Unterprogramme vor main in die Datei. Dies muß jedoch wiederum so geschehen, dass Funktionen zum Zeitpunkt ihres Aufrufes bereits bekannt sind! Wo dies nicht möglich ist (z.B. sich gegenseitig aufrufende, rekursive Unterprogramme), oder wenn Sie das stört, müssen Sie Prototypen verwenden. Wie definiert man nun Prototypen? Sie kopieren einfach die erste Zeile des Unterprogrammes (z.B. "void up1(char Zeichen)") und fügen es an einer geeigneten Stelle ein (so, dass alle Aufrufe später in der Datei kommen). Beachten Sie jedoch, daß Prototypen einen Strichpunkt am Ende haben!

#include <conio.h>
#include <stdio.h>

void ausgeben(int Zahl);  //Der Prototyp

void main(void)
{
   clrscr();
   ausgeben(12);
   getch();
}

void ausgeben(int Zahl)   //Die eigentliche Prozedur
{
  printf("Ausgabe: %d\n",Zahl);
}


Besondere Datentypen

Felder

Oft muß man sehr viele Werte gleichzeitig abspeichern und betrachten, die alle der selben Aufgabe dienen. Man schreibt z.B. ein Programm, das 10 Zahlen einlesen und anschließend wieder ausgeben soll. Man könnte das natürlich mit 10 einzelnen Variablen bewerkstelligen, aber es ist sinnvoller, dabei Felder, sogenannte "ARRAY"s, zu verwenden.

In einem Array werden mehrere Variablen gleichen Typs zusammengefasst und hintereinander im Speicher abgelegt. So kann man viele tausend Variablen anlegen mit nur einer Zeile Code. Doch es gibt noch größere Vorteile: Sie können das Array mit z.B. einer for-Schleife ganz einfach nach Werten durchsuchen. Stellen Sie sich vor, Sie müssten mit 100 verschiedenen Variablen Zahl_001 bis Zahl_100 arbeiten! Ein ihnen bereits bekanntes Beispiel für ein Array ist ein String. Ein String ist nichts anderes als ein Array des Datentypes char.

Syntax: Dantentyp Variablenname[Anzahl]

Der Name muß natürlich ein gültiger Bezeichner sein, als Datentyp kann jeder Typ genommen werden. In der eckigen Klammer wird die Anzahl der Elemente bekanntgegeben. Ein mit [3] definiertes Array hat 3 Variablen, nämlich [0], [1] und [2] (kein [3], da immer mit [0] begonnen wird). Um auf eine der im Array enthaltenen Variablen zugreifen zu können, müssen Sie den Variablennamen und in eckigen Klammern den Index (die "Nummer") der Variablen angeben. Diese Variable verhält sich dann wie eine ganz normale Variable des jeweiligen Datentypes.

#include <conio.h>
#include <stdio.h>

void main(void)
{
  int Zaeler;
  int Zahlen[10];  //Zahlen[0] bis Zahlen[9] !!!
  clrscr();
  for (Zaeler=0; Zaeler<10; Zaeler++)
  {
    printf("Bitte Zahl %d eingeben: ",Zaeler);
    scanf("%d",Zahlen[Zaeler]);
    printf("\n");
  }
  printf("Danke!\n");
  for (Zaeler=0; Zaeler<10; Zaeler++) printf("Zahl %d war: %d",Zaeler,Zahlen[Zaeler]);
END.

Bemerkungen: Zuerst wird ein 10 int-Variablen großes Array angelegt. In dieses wird nun der Reihe nach 10 Zahlen eingelesen. Anschließend werden alle 10 Zahlen ausgegeben.

ACHTUNG!: Wenn Sie einen ungültigen Index angeben (einen, der in der Deklaration nicht enthalten war) können je nach Compiler und Einstellung mehrere Dinge geschehen: entweder liefert der Compiler einen "Bereichsüberschreitungs-" Fehler, oder das Programm funktioniert nicht oder falsch (im schlimmsten Fall könnte sogar der Computer abstürzen). Also achten Sie darauf, daß Sie keine ungültigen Werte als Index angeben!

Mehrdimensionale Felder

Manchmal benötigen mehr als nur ein ein-dimensionales Array, wie Sie es bisher kennengelernt haben. Auch dies ist kein Problem. In der Deklaration geben Sie einfach mehrere eckige Klammernhintereinander an. Aber Vorsicht: der Speicherplatz ist begrenzt, ein "char feld[1024][1024]" hat die Speicherplatzgrenzen bereits weit überschritten, und der Compiler wird einen (bei gewissen Einstellung auch keinen) Fehler liefern. Beim Zuweisen von Werten etc. müssen Sie bei mehrdimensionalen Feldern auch mehr als einen Index angeben, die Erklärung folgt als Beispiel:

#include <conio.h>
#include <stdio.h>

void main(void)
{
  int x,y;
  int feld[3][5];
 
  for(x=0; x<3; x++) for (y=0; y<5; y++)
  {
    printf("Feldwert X: %d Y: %d",x,y);
    scanf("%d",feld[x][y]);
    printf("\n");
  }
  clrscr();
  for(x=0; x<3; x++) for (y=0; y<5; y++) printf("Wert: feld[%d][%d]= %d\n",x,y,feld[x][y]);
}

Erklärung:

Zuerst wird ein 3 mal 5 int Array angelegt. Dann werden die Werte eingegeben: zuerst feld[1][1], dann feld[1][2], usw. bis feld[3][5]. Zum Schluß werden alle Werte noch einmal ausgegeben.

Strings

Im Kapitel Variablen und Datentypen haben Sie bereits etwas von Strings gehört, mangels Wissen über Felder und Unterprogramme aber nicht sehr ausführlich. Hier folgt nun die ausführlichere Erklärung. Bei Strings handelt es sich um char Felder. Wenn Sie einen String definieren, müssen Sie das mittels char machen:

char string[21];

Nun haben Sie eine string, in dem Sie 21 Zeichen speichern können. Ganz richtig ist das jedoch nicht. C arbeitet mit "null-terminierten Strings". Das beudeutet, dass die Länge des Strings nicht abgespeichert wird (wie in Pascal), sondern ein Zeichen innerhalb des Strings das Ende desselben angibt (ASCII Nr. 0, daher der Name "null terminiert"). Das letzte Zeichen eines Strings muß daher immer das ASCII-Zeichen Nr. 0 sein. Ist es das nicht, hat der String kein definiertes Ende, und wenn Sie versuchen, ihn auszugeben, könnte es eine Weile dauern, bis sich im Speicher zufällig irgendwo eine 0 befindet. Es stehen ihnen daher (im Beispiel) nur 20 Zeichen zur Verfügung.

strcpy

Sie können einem String nicht direkt einen Wert (Text) zuweisen. Dazu müssen Sie die Prozedur strcpy benutzen. Diese erwartet als ersten Parameter eine String-Variable (ohne eckige Klammern) und als zweiten Parameter einen String. Dieser String kann sein: eine andere String-Variable, oder ein in doppelten Hochkommas (") eingeschlossener Text. Die Funktion fügt am Ende automatisch ein 0-Zeichen ein. Um diese Funktion nutzen zu können, müssen Sie die Datei string.h includieren!

#include <conio.h>
#include <stdio.h>
#include <string.h>

void main(void)
{
  char stri1[21],eingabe[21];

  strcpy(stri1,"hallo");
  clrscr();
  printf("Der 1. String: %s\n",stri1);
  printf("Bitte geben Sie maximal 20 Zeichen ein: ");
  scanf("%s",eingabe);
  strcpy(stri1,eingabe);
  printf("\n %s = %s",stri1,eingabe);
  getch();
}

Hinweis: Da ein String, wie jedes Feld, eigentlich ein Zeiger ist, brauchen Sie kein & bei scanf angeben!

Erklärung: Es werden 2 gleich große Strings definiert: stri1 und eingabe, mit je 20 "nutzbaren" Zeichen. stri1 wird der Wert "hallo" zugewiesen. Das 0-Zeichen wird automatisch angefügt! Der String wird ausgegeben. Als neues "Sonderzeichen" kommt %s ins Spiel. Es hat die gleiche Aufgabe wie %d oder %c, nur halt für Strings. Sie werden gebeten, eine String einzugeben. Dieser String wird danach in die Variable stri1 kopiert. Beide Strings (die ja auch den selben Wert haben) werden ausgegeben.

strlen

Die Funktion strlen, die als Parameter eine String-Variable erwartet, liefert die Länge diese Strings zurück. Sie werden jetzt vermutlich sagen: "DAs ist doch klar, wie lang der String ist. Ich habe es ja bei der Deklaratin angegeben". Das stimmt schon, aber denken Sie noch einmal an die null-terminierten Strings. Das 0-Zeichen steht am Ende des Strings (am Ende der gültigen Zeichenfolge), aber nicht unbedingt am Ende des reservierten Speicherplatzes. Haben Sie eine Variable "char Variable[21];", und ihr den Wert "hallo" zugewiesen, dann steht das null-Zeichen in Variable[5]. Der "gültige" String ist also 5 Zeichen (0-4) lang. Und genau das (5) würde strlen zurück liefern.

#include <conio.h>
#include <stdio.h>
#include <string.h>

void main(void)
{
  char stri[21];
  strcpy(stri,"hallo");
  printf("Der String ist %d Zeichen lang", strlen(stri) );
  getch();
}

Diese Funktion wird vor allem gebraucht, wenn Sie direkt auf den String zugreifen, mittels stri[0], stri[1], etc.


Strukturen

In C können Sie sogenannte "Strukturen" definieren. Dabei handelt es sich um eine Zusammenfassung von mehreren Datentypen zu einem größeren. Im Unterschied zu Feldern können in Strukturen jedoch unterschiedliche Datentypen gespeichert werden:

#include <conio.h>
#include <stdio.h>

struct {
  char vname[20],nname[20];
  char telnr[15];
  int alter;
} PERSON;

PERSON hubert;
PERSON stefan;

Erklärung: "struct {" leitet die Definition ein. Dann werden 4 Variablen angelege: 3 Strings und ein int. mit } wird der Block wieder geschlossen, anschließend muß der Struktur ein Name zugewiesen werden (z.B. PERSON). Sie haben damit einen Datentyp erstellt. Um eine Variable dieses Typs (PERSON) anzulegen, geben Sie einfach "PERSON Variablenname;" an. Hinweis: Das der Datentyp groß geschrieben wird, ist nicht notwendig. Es dient, wie schon so oft, nur der Übersichtlichkeit. Wie spricht man die einzelnen Variablen einer Struktur nun an? Das funktioniert so:

Zuerst gibt man die Struktur-Variable an, im Beispiel "hubert" oder "stefan". Danach schreibt man einen Punkt. Zum Schluß folgt die gewünschte Variable, also z.B. vname. Bemerkung: Die Anwendung von Strukturen verdeutlicht folgendes Beispiel:

strcpy (hubert.Vorname, "Max");
strcpy (Person1.Nachname,"Mustermann");
Readln(Person1.strasse);

Zeiger

Jede Variable steht an einer genau definierten stelle im Speicher. Diese Stelle kann man in erfahrung bringen. Wie man das macht und was das überhaupt für Vorteile bringt, erfahren Sie in diesem Abschnitt. Ein Zeiger ist im Prinzip eine (int-) Variable. In ihr ist jedoch keine Zahl gespeichert, sondern eine "Adresse". Diese gibt eine bestimmte Position im Arbeitsspeicher dar. Hier sehen Sie, wie man einen Zeiger definiert und mit ihm arbeitet:

#include <conio.h>
#include <stdio.h>

void main(void)
{
  int Zahl;
  int *Zeiger;
 
  Zeiger=&Zahl;
  *Zeiger=12;
  
  printf("%d = %d", Zahl, *Zeiger);

Erklärung: Um einen Zeiger anzulegen, gibt man folgendes an: Datentyp, dann einen Stern (*), anschließend den Namen (und einen Strichpunkt). Um einen Zeiger auf eine bestimmte Zahl "zeigen" zu lassen, muß man dem Zeiger die Adresse einer Variablen zuordnen. Das funktioniert mittels dem Adress-Operator & (Zeiger=&Zahl). Jetzt möchten Sie dem Zeiger einen Wert zuweisen. Aber natürlich nicht dem Zeiger, sondern der Variablen, auf die der Zeiger "zeigt". Na, "Zahl=Wert;", werden Sie sagen. Aber wozu dann Zeiger? Na, ja, jednefalls geht das mittels des "Inhalts-Operators" * (*Zeiger=12). Genauso können Sie mit dem Inhaltsoperator Werte abfragen und an printf (und jedes andere Unterprogramm) übergeben.

Zeiger als Parameter

Wenn Sie ein Unterprogramm aufrufen, können Sie diesem Parameter übergeben, aber keine Werte zurückgekommen (außer den Funktionswert bei Funktionen). Dies hat einen guten Grund: beim Aufruf werden nicht die aufgerufenen Parameter benutzt, sondern es werden deren Werte in neue Variablen kopiert. Diese Variablen werden am Ende des Unterprogrammes "zerstört", ohne ihre Werte an die aufrufenden Parameter zu übergeben. Jede Veränderung eines Parameters hat daher keine Auswirkung auf den Parameter. Doch was ist, wenn Sie Parameter in Unterprogrammen verändern möchten? Ganz einfach, Sie verwenden Zeiger. Der Computer legt dann immer noch Kopien an. In dieser Kopie steht aber kein Wert, sondern die Adresse einer Varaiblen. Und auf diese können Sie dann zugreifen. Denken Sie nur an scanf - da übergeben Sie ja auch die Adresse einer Variablen ("&Variable").

#include <conio.h>
#include <stdio.h>

void erhoehe(int *zeiger)
{
  *zeiger=*zeiger+1;
}

void main(void)
{
  int Zahl;
  printf("Zahl eingeben: ");
  scanf("%d",&Zahl);
  erhoehe(&Zahl);
  printf("\nDie erhoehte Zahl lautet: %d\n",Zahl);
  getch();
}

Parameter von Main

Das Unterprogramm "main" kann, wie jede andere Funktion, Parameter besitzen. Doch keine selbst gewählten, sondern nur bestimmte. Doch warum braucht main Parameter? Denken Sie einmal an alle Betriebssystembefehle: "dir *.exe", "copy *.* a:" oder "ls -la". All diese Befehle sind aus 2 Teilen aufgebaut: Befehl und Parameter. Und genau diese Parameter können Sie mit den main-Parametern abfragen.

void main(int argc, char *argv[])

Bei "argc" handelt es sich um eine normale int Variable (engl. "Argument count", Parameter-Zähler"). In ihm steht die Anzahl der angegeben Parameter.

#include <conio.h>
#include <stdio.h>

void main(int argc,char *argv[])
{
  int i;

  clrscr();
  printf("Es wurden %d Parameter angegeben",argc);
  for (i=0; i<argc;i++) printf("Parameter %d: %s\n",i,argv[i]);
  getch();
}

Erklärung: Bei der 1. Ausgabe wird ausgegeben, wie viele Parameter insgesammt angegeben wurden. Aber Achtung: Es gibt immer mindestens einen Parameter! In argc[0] steht nämlich die aufgerufene EXE-Datei. Außerdem: da die Liste bei argv[0] anfängt, ist der maximal gültige Parameter argv[argc-1]! In der For-Schleife werden alle Parameter, inklusive ihrer Nummer, ausgegeben. Experimentieren Sie hier mit Parametern, um das System zu vertehen!


Autoren

  • Plasma
  • Bernd

Quellen:

  • Kernighan und Ritchie - Buch
  • Christian Wirth , C Tutorial
  • Prof. Dr. J. Dankert Ausführungen


Siehe auch

Weblinks


LiFePO4 Speicher Test