Programmieren in C

Wenn wir uns schon mit unixoiden Systemen (wie GNU/Linux) beschäftigen sollten wir uns auch ein wenig um C kümmern:

Bjarne Stroustrup (der Schöpfer von C++) sagte über den Vergleich von C und C++:
"C makes it easy to shoot yourself in the foot, C++ makes it harder, but when you do, it blows away your whole leg!"

C ist eine Programmiersprache, die in ihren Grundzügen nur aus wenigen Befehlen aufgebaut ist. Das ist eine ihrer großen Stärken, da man sich später eigene Befehle programmieren kann. Das heißt aber auch: Selbst Grundfunktionen wie Bildschirmausgabe oder die Tastatureingabe sind kleine Unterprogramme, die explizit eingebunden werden müssen, da sonst der Compiler nicht weiß, was er zu tun hat. Fazit: C ist modular aufgebaut. Und zwar von Grund auf!

Wegen der Nähe der Sprache C zur Hardware, einer vormals wichtigen Eigenschaft um Unix leichter portierbar zu machen, ist C von Programmierern häufig auch als ein "Hochsprachen-Assembler" bezeichnet worden.

C selbst bietet in seiner Standardbibliothek nur rudimentäre Funktionen an. Die Standardbibliothek bietet hauptsächlich Funktionen für die Ein-/Ausgabe, Datei-handling, Zeichenkettenverarbeitung, Mathematik, Speicherreservierung und einiges mehr. Sämtliche Funktionen sind auf allen C-Compilern verfügbar. Jeder Compiler-Hersteller kann aber weitere Programmbibliotheken hinzufügen. Programme, die diese benutzen, sind dann allerdings nicht mehr portabel.

Das Haupteinsatzgebiet von C liegt in der Systemprogrammierung, einschließlich der Erstellung von Betriebssystemen und der Programmierung von Embedded Systems und Microcontroller bei denen Speicherplatz sehr kostbar ist. Der Grund liegt in der Kombination von erwünschten Charakteristiken wie Portabilität und Effizienz mit der Möglichkeit, Hardware direkt anzusprechen und dabei niedrige Anforderungen an die Laufzeitumgebung zu haben.

Wegen der relativ hohen Geschwindigkeit und geringen Codegröße werden Compiler, Programmbibliotheken und Interpreter anderer höherer Programmiersprachen (wie z. B. die Java Virtual Machine) oft in C implementiert.

Ein C-Programm wird gewöhnlich mithilfe eines Compilers aus einer oder mehreren einfachen Textdateien zu Objektcodedateien übersetzt. Mehrere Objektcodedateien wiederum werden anschließend von einem Linker (auch Binder genannt) zu einer ausführbaren Datei gebunden. Jedes ausführbare C-Programm besitzt eine Hauptfunktion (in C wird sie main() genannt). Natürlich können Sie auch eigene Bibliotheken erstellen und diese in einem Archiv zusammenfassen. Bibliotheken wiederum können Sie bei einem späteren Bindevorgang des Linkers wieder verwenden, damit diese zum Programm hinzugebunden werden.

.

Ein erstes kleines Programm

Unter Linux ist es sehr einfach C Programme zu erstellen. Die meisten Derivate bringen "gcc" ( gnu compiler collection ) bereits mit. Ansonsten, wie gehabt, mittels Synaptic Paketverwaltung oder apt-get einfach zu installieren.

In einem Texteditor unserer Wahl geben wir folgenden Code ein:

#include <stdio.h>

main (){

puts(“Dies ist unser erstes C Programm unter Linux”);

/* unter Windows müsste man hier ein "getchar()" setzen weil sich die Konsole anderfalls direkt wieder schliesst */

return 0;

}

Wir speichern den Text unter, z.B. „ erstes.c“, und geben folgendes in unsere Konsole ein:

debian% gcc erstes.c  -o erstes

debian% ./erstes

Dies ist unser erstes C Programm unter Linux
Das gleiche Programm in:

C++

#include <iostream>

using namespace std;
int main()
{
 cout << "Dies ist unser erstes C Programm unter Linux" << endl;
 return 0;
}

Man erkennt deutlich die Unterschiede!

#include <stdio.h>
int main(void)
{
    int i;
    //int z = i*i;
    
    printf( "Geben Sie bitte einen Zahlenwert ein: ");
    scanf("%d", &i);
    printf( "Das Quadrat von %d  ist %d \n" , i, i*i);
    return 0;
}

In C

#include <stdio.h>

int main(void)
{
int i;
printf( "Geben Sie bitte einen Zahlenwert ein: ");
scanf("%d", &i);
printf( "Das Quadrat von %d  ist %d \n" , i, i*i);
return 0;
}

In C++

#include <iostream>

using namespace std;
int main()
{
    int i;
    cout << "Geben Sie bitte einen Zahlenwert ein: ";
    cin >> i;
    cout << "Das Quadrat von " << i << " ist " << i*i << endl;
    return 0;
}

In C-Programmen müssen alle Variablen deklariert werden. Man kann diese aber auch zusammen fassen wenn sie gleichen Typs sind.

main( ) {
int a, b, c, summe;
a = 6;  b = 5;  c = 1 ;
summe = a + b + c;
printf("Die Summe ist:  %d", sum);
}

%d signalisiert, dass das nächste Argument  in der Liste der Argumente als Dezimalwert ausgegeben wird.

Funktionen

Als Nächstes eine kleine Einführung in Funktionen.

In C++ muss die Funktion immer vor  "main"  deklariert werden, oder man muss die Funktion vor main "prototypen"!

Ein Beispiel für C++:

#include <iostream>
using namespace std;

int zahl1;
int zahl2;
int addNumbers(int x, int y);

int main() {
cout << "Geben Sie eine Zahl ein. " ;
cin >> zahl1;
cout << "Geben Sie eine zweite Zahl ein: ";
cin >> zahl2;
cout << "Das Ergebnis lautet " << addNumbers(zahl1, zahl2) << endl;
return 0;
}

int addNumbers(int x, int y) {

return x+y;

}

Weiter mit C:

Das Unterprogramm, hier "flaeche" wird ausserhalb der "main-Funktion" deklariert, wobei egal ist ob vorher oder danach.

# include <stdio.h>

int flaeche (int x, int y);

int main (void)

{
int eins;
int zwei;
printf("Geben Sie die erste Seitenlänge der Fläche ein: ");
scanf("%d", &eins);
printf("Geben Sie die zweite Seitenlänge der Fläche ein: ");
scanf("%d", &zwei);
printf("Das Ergebnis lautet: %d", flaeche(eins, zwei));
return 0;
}

int flaeche (int x, int y)
{
return x*y;
}

Ich könnte natürlich auch das Beispiel von oben als Funktion schreiben. Das würde dann wie folgt aussehen:

#include <stdio.h>
int main(void)
{
    int i;
    //int z = i*i;
    
    printf( "Geben Sie bitte einen Zahlenwert ein: ");
    scanf("%d", &i);
    printf( "Das Quadrat von %d  ist %d \n" , i, quadrat(i));
    return 0;
}
int quadrat(int a)
{
    return a*a;
}
#include <stdio.h>
int main(void) {
int i;
printf( "Geben Sie bitte einen Zahlenwert ein: ");
scanf("%d", &i);
printf( "Das Quadrat von %d  ist %d \n" , i, quadrat(i));
return 0;
}
int quadrat(int a)
{
return a*a;
}

Ein weiteres Beispiel wie Funktionen in einem C-Programm eingebunden werden.

#include <stdio.h>

int a = 10, b = 12;
void demo (void);
void test (void);
int main (void)
{
printf("\nVor dem Aufruf von demo(), a = %d und b = %d. ", a, b);
demo();
printf("\nNach dem Aufruf von demo(), a = %d und b = %d.", a, b);
test();
return 0;
}
void demo(void)
{
int a = 88, b = 99;
printf("\nIn der Funktion demo(), a = %d und b = %d.", a, b);
}
void test(void)
{
printf("\nNoch eine Funktion!\n");
}

Die Variablen a und b werden ausserhalb einer Funktion deklariert und sind somit global. In der Funktion "demo" sind eigene, lokale Variablen a und b deklariert, die beim Aufruf der Funktion auch angezeigt werden. Die Funktionen übernehmen keine Parameter und haben deshalb "void" im Prototyp stehen. Da sie auch keine Werte zurückgeben lautet der Typ des Rückgabewertes ebenso "void". Nach Verlassen der Funktion "demo" werden erneut die globalen Variablen ausgegeben. Wir lassen das Programm laufen und erhalten folgende Ausgabe:

linux% gcc demo.c -o demo
linux% ./demo

Vor dem Aufruf von demo(), a = 10 und b = 12.
In der Funktion demo(), a = 88 und b = 99.
Nach dem Aufruf von demo(), a = 10 und b = 12.
Noch eine Funktion!

Pointer

Eine Variable wird intern immer über eine Adresse angesprochen (außer die Variable befindet sich bereits in einem Prozessregister). Alle Speicherzellen innerhalb eines Arbeitsspeichers erhalten eine eindeutige Adresse. Immer wenn der Prozessor einen Wert aus dem RAM liest oder schreibt, schickt er diese Adresse an den Arbeitsspeicher.

In einem Beispiel lassen wir uns die Adresse einer Variablen ausgeben:

#include<stdio.h>
int finger = 10;
int *zeiger;

int main (void)

{
zeiger = &finger;
printf("\nDie Speicheradresse von finger lautet: = %p\n", zeiger);
return 0;
}

Mit dem * initialisieren wir die Variable "zeiger" als Pointer. Danach bestimmen wir, daß "zeiger" auf die Adresse von "finger" zeigen soll.
Mit printf geben wir die Speicheradresse schließlich aus.( Darauf achten, daß die Adresse mit %p formatiert wird. In vielen Tutorien steht dort etwas Anderes. Unter gcc funktioniert das allerdings nicht.)

Wir ändern das Programm "demo" ein wenig um noch einmal auf Pointer einzugehen:

#include <stdio.h>
void demo(int b)
{
b = (b * 10);
printf("\nIn der Funktion demo(), b = %d.\n", b);
}
int main (void)
{
int a = 10;
demo(a);
printf("Ohne Pointer ist a: %d\n", a);
return 0;
}

In der Funktion demo(), b = 100.
Ohne Pointer ist a: 10

Innerhalb von demo wird die Variable geändert. Aber nur lokal auf diese Funktion bezogen. In der übergeordneten, aufrufenden Funktion bleibt der Anfangswert erhalten.
Manchmal ist es aber auch notwendig diese Variable zu ändern. Dies erreichen wir indem wir auf den Speicherort des Wertes zugreifen.

#include <stdio.h>
void demo(int *b)
{
*b = (*b * 10);
printf("\nIn der Funktion demo(), b = %d.\n", *b);
}
int main (void)
{
int a = 10;
demo(&a);
printf("Mit Pointer ist a: %d\n", a);
return 0;
}

In der Funktion demo(), b = 100.
Mit Pointer ist a: 100


Wie man sieht, wurde die Variable "a"  in der aufrufenden Funktion verändert, die Adresse der Variablen a wird mit "&" in die Funktion demo(&a) hineingegeben und dort mit dem * Operator dereferenziert und verändert. Weil man hier nicht die Variable sondern die Referenz, die Adresse der Variablen, übergibt nennt sich das ganze Call by Reference.

Überlegungen zu Strings

Ein kleines Beispielprogramm mit nachfolgender Ausgabe im Terminal:

#include <stdio.h>

int main()
{
char string1[100]={"Dein vollständiger Name lautet: "};
char Eingabe[100];
char string2[100]={"Danach wird noch dies ausgegeben!"};

printf("String-Beispiel:\nBitte gib Deinen vollständigen Namen an : ");
fgets(Eingabe, 100, stdin);
printf("%s%s%s\n", string1, Eingabe, string2);
return 0;
}

String-Beispiel:
Bitte gib Deinen vollständigen Namen an : Claus Clausen
Dein vollständiger Name lautet: Claus Clausen
Danach wird noch dies ausgegeben!

Zunächst werden die 3 Strings: string1, string2, und Eingabe deklariert. Danach wird der User aufgefordert seinen vollständigen Namen einzugeben.
Wir verwenden die Eingabefunktion "fgets" um diesen Eingabestring einzulesen.
fgets hat folgende Struktur:
fgets(Stringname, Stringlänge, Standartstream); der Standartstream ist hier "stdin" (Standart-Eingabe).

Übrigens: Alle hier veröffentlichten Programme funktionieren so nur auf einem GNU/Linux System!
Windows-User müßten sie ein wenig abwandeln.

Mit der Funktion sprintf kann ich Zahlen zu einem String konvertieren, und das funktioniert folgendermassen:

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

int main()
{
int Zahl=12345;
char String[5];
sprintf(String,"%d",Zahl);
printf("Der Wert von der Variable String: %s\n",String);

return 0;
}

Einfach mal ausprobieren was ausgegeben wird wenn wir sprintf auskommentieren! 


}}
}

Windows-Programmierung

Für alle Besucher meiner Seite die mit Visual Studio unter Windows arbeiten, ein kleines Schmankerl.
Eine Message Box unter C, die funktioniert!
Ich gebe zu, der Text ist nicht besonders einfallsreich. Ihr seid da bestimmt etwas kreativer!

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
 
int main()
{
    int Antwort = MessageBox(
        NULL,
        (LPCWSTR)L" Hast du heute schon in C programmiert?",
        (LPCWSTR)L"C Windows Programm",
        MB_ICONQUESTION | MB_YESNO );
 
if(IDYES==Antwort)
{
    printf("Das hoere ich gern!");
}
if(IDNO==Antwort)
{
    printf("Das kann ich nicht glauben!");
}
printf("\n\n");
 
system("PAUSE");
return 0;
}