2.4. Kompilieren mit dem cc

Dieser Abschnitt behandelt ausschließlich den GNU Compiler für C und C++, da dieser bereits im Basissystem von FreeBSD enthalten ist. Er kann mittels cc oder gcc aufgerufen werden. Die Details zur Erstellung einer Anwendung mit einem Interpreter variieren zwischen verschiedenen Interpretern mehr oder weniger stark, und werden meist ausführlich in der zugehörigen Dokumentation oder Online-Hilfe beschrieben.

Sobald Sie Ihr Meisterwerk fertig geschrieben haben besteht der nächste Schritt darin, dieses (hoffentlich!) unter FreeBSD zum Laufen zu bekommen. Dies beinhaltet üblicherweise mehrere Schritte, wobei jeder einzelne Schritt von einem separaten Programm durchgeführt wird.

  1. Aufbereiten Ihres Quelltextes durch Entfernen von Kommentaren, sowie weiteren Tricks wie das Ersetzen von Macros in C.

  2. Überprüfen der Syntax Ihres Quelltextes, um die Einhaltung der Sprachregeln sicherzustellen. Wenn Sie diese verletzt haben werden entsprechende Fehlermeldungen Ihnen dies mitteilen!

  3. Übersetzen des Quelltextes in Assemblersprache —diese ist dem eigentlichen Maschinencode schon sehr nahe, jedoch immer noch für Menschen lesbar. Angeblich. [1]

  4. Übersetzen der Assemblersprache in Maschinencode—genau, wir sprechen hier von Bits und Bytes, Einsen und Nullen.

  5. Überprüfen, ob Sie Dinge wie Funktionen und globale Variablen in einheitlicher Weise verwendet haben. Wenn Sie z.B. eine nicht existierende Funktion aufgerufen haben, wird eine entsprechende Fehlermeldung Ihnen dies mitteilen.

  6. Wenn aus mehreren Quelltextdateien eine ausführbare Datei erstellt werden soll wird herausgefunden, wie die einzelnen Codeteile zusammengefügt werden müssen.

  7. Ausarbeiten, wie das Programm aussehen muss, damit der Lader zur Laufzeit des Systems dieses in den Speicher laden und ausführen kann.

  8. Endgültiges Schreiben der ausführbaren Datei in das Dateisystem.

Das Wort kompilieren wird häufig für die Schritte 1 bis 4 verwendet—die anderen werden mit dem Wort verlinken zusammengefasst. Manchmal wird Schritt 1 auch als Pre-Processing und die Schritte 3-4 als assemblieren bezeichnet.

Glücklicherweise werden alle diese Details vor Ihnen verborgen, da cc ein Frontend ist, welches sich um die Ausführung all dieser Programme mit den richtigen Argumenten für Sie kümmert; einfaches eingeben von

% cc foobar.c

führt zur Übersetzung von foobar.c durch alle bereits erwähnten Schritte. Wenn Sie mehr als eine Datei übersetzen wollen müssen Sie etwas wie folgt eingeben

% cc foo.c bar.c

Beachten Sie, daß die Überprüfung der Syntax genau dies tut—das reine Überprüfen der Syntax. Es findet keine Überprüfung bzgl. logischer Fehler statt, die Sie vielleicht gemacht haben, wie z.B. das Programm in eine Endlosschleife zu versetzen, oder Bubble Sort zu verwenden, wenn Sie eigentlich Binary Sort benutzen wollten. [2]

Es gibt haufenweise Optionen für cc, die alle in der zugehörigen Manualpage beschrieben werden. Im Folgenden werden ein paar der wichtigsten Optionen mit Beispielen ihrer Anwendung gezeigt.

-o filename

Die Name der Ausgabedatei. Wenn Sie diese Option nicht verwenden erstellt cc eine Datei mit dem Namen a.out. [3]

% cc foobar.c               executable is a.out
% cc -o foobar foobar.c     executable is foobar
	   
-c

Dies kompiliert die Datei nur, verlinkt sie jedoch nicht. Nützlich für Spielereien, um die Syntax auf Korrektheit zu überprüfen, oder falls Sie ein Makefile verwenden.

% cc -c foobar.c
	   

Dieser Befehl erzeugt eine Objektdatei (nicht ausführbar) mit den Namen foobar.o. Diese kann mit anderen Objektdateien zusammen zu einer ausführbaren Datei verlinkt werden.

-g

Diese Option erzeugt die Debug-Version einer ausführbaren Datei. Dabei fügt der Compiler zusätzliche Informationen darüber, welcher Funktionsaufruf zu welcher Zeile im Quelltext gehört, der ausführbaren Datei hinzu. Ein Debugger kann Ihnen mit Hilfe dieser Information den zugehörigen Quelltext anzeigen, während Sie den Programmverlauf schrittweise verfolgen, was sehr hilfreich sein kann; der Nachteil dabei ist, daß durch die zusätzlichen Informationen das Programm viel größer wird. Normalerweise verwendet man die Option -g während der Entwicklung eines Programms, und für die “Release-Version”, wenn man von der Korrektheit des Programms überzeugt ist, kompiliert man das Programm dann ohne diese Option.

% cc -g foobar.c
	   

Mit diesem Befehl wird eine Debug-Version des Programms erzeugt. [4]

-O

Diese Option erzeugt eine optimierte Version der ausführbaren Datei. Der Compiler verwendet einige clevere Tricks, um das erzeugte Programm schneller zu machen. Sie können hinter der Option -O eine Zahl angeben, um eine höheres Level der Optimierung festzulegen. Dadurch wird jedoch häufig eine fehlerhafte Optimierung seitens des Compilers aufgedeckt. Zum Beispiel erzeugte die Version des cc, welche mit dem FreeBSD Release 2.1.0 mitgeliefert wurde, bei Verwendung der Option -O2 unter bestimmten Umständen falschen Code.

Optimierungen werden normalerweise nur beim Kompilieren von Release-Versionen aktiviert.

% cc -O -o foobar foobar.c
	   

Durch diesen Befehl wird eine optimierte Version von foobar erzeugt.

Die folgenden drei Flags zwingen den cc dazu, Ihren Code auf die Einhaltung der internationalen Standards hin zu überprüfen, welche häufig als ANSI Standards bezeichnet werden, obwohl sie streng genommen zum ISO Standard gehören.

-Wall

Aktivieren aller Warnmeldungen, die die Autoren des cc für wichtig halten. Trotz des Namens dieser Option werden dadurch nicht sämtliche Warnungen ausgegeben, die der cc ausgeben könnte.

-ansi

Deaktivieren der meisten, jedoch nicht aller, nicht-ANSI C Eigenschaften, die der cc bietet. Trotz des Namens ist durch diese Option nicht sichergestellt, daß Ihr Code diese Standards auch vollständig einhält.

-pedantic

Deaktivieren aller Eigenschaften des cc, welche nicht konform zu ANSI C sind.

Ohne diese Flags wird Ihnen der cc die Verwendung eigener Erweiterungen des Standards erlauben. Einige dieser Erweiterungen sind zwar sehr nützlich, werden jedoch nicht von anderen Compilern unterstützt—eigentlich ist eines der Hauptziele des Standards, das Leute Code so schreiben können, daß dieser mit jedem Compiler auf beliebigen Systemen funktioniert. Dies wird häufig als portabler Code bezeichnet.

Im Allgemeinen sollten Sie versuchen, Ihren Code so portabel wie möglich zu schreiben, da Sie ansonsten eventuell das gesamte Programm noch einmal neu schreiben müssen, falls dieser in einer anderen Umgebung laufen soll—und wer weiß schon was er in ein paar Jahren verwenden wird?

% cc -Wall -ansi -pedantic -o foobar foobar.c

Durch diesen Befehl wird eine ausführbare Datei namens foobar erzeugt, nachdem foobar.c auf die Einhaltung der Standards überprüft wurde.

-llibrary

Mit dieser Option kann eine Bibliothek mit Funktionen angegeben werden, die während des Verlinkens verwendet wird.

Das am häufigsten auftretende Beispiel dieser Option ist die Übersetzung eines Programmes, welches einige der mathematischen Funktionen in C verwendet. Im Gegensatz zu den meisten anderen Plattformen befinden sich diese Funktionen in einer separaten Bibliothek, deren Verwendung Sie dem Compiler explizit mitteilen müssen.

Angenommen eine Bibliothek heißt libirgendwas.a, dann müssen Sie dem cc als Argument -lirgendwas übergeben. Zum Beispiel heißt die Mathematik-Bibliothek libm.a, und daher müssen Sie dem cc als Argument -lm übergeben. Ein typisches “Manko” der Mathematik-Bibliothek ist, daß diese immer die letzte Bibliothek auf der Kommandozeile sein muß.

% cc -o foobar foobar.c -lm
	   

Durch diesen Befehl werden die Funktionen aus der Mathematik-Bibliothek in foobar gelinkt.

Wenn Sie C++-Code kompilieren wollen, müssen Sie -lg++, bzw. -lstdc++ falls Sie FreeBSD 2.2 oder neuer verwenden, zu Ihrer Kommandozeile hinzufügen, um Ihr Programm gegen die Funktionen der C++ Bibliothek zu linken. Alternativ können Sie anstatt cc auch c++ aufrufen, welcher dies für Sie erledigt. c++ kann unter FreeBSD auch als g++ aufgerufen werden.

% cc -o foobar foobar.cc -lg++     Bei FreeBSD 2.1.6 oder älter
% cc -o foobar foobar.cc -lstdc++  Bei FreeBSD 2.2 und neuer
% c++ -o foobar foobar.cc
	   

Beide Varianten erzeugen eine ausführbare foobar aus der C++ Quelltextdatei foobar.cc. Beachten Sie bitte, daß auf UNIX® Systemen C++ Quelltextdateien üblicherweise auf .C, .cxx oder .cc enden, und nicht wie bei MS-DOS® auf .cpp (welche schon anderweitig benutzt wurde). Der gcc hat normalerweise anhand dieser Information entschieden, welcher Compiler für die Quelltextdatei zum Einsatz kommen soll; allerdings gilt diese Einschränkung jetzt nicht mehr, und Sie können Ihre C++-Dateien ungestraft auf .cpp enden lassen!

2.4.1. Häufig auftretende cc-Fragen und -Probleme

2.4.1.1. Ich versuche ein Programm zu schreiben, welches die Funktion sin() verwendet, erhalte jedoch eine Fehlermeldung. Was bedeutet diese?
2.4.1.2. So, ich habe jetzt dieses einfache Programm als Übung für -lm geschrieben. Alles was es macht ist, 2.1 hoch 6 zu berechnen.
2.4.1.3. Wie kann ich das korrigieren?
2.4.1.4. Ich habe eine Datei mit dem Namen foobar.c kompiliert, kann jedoch nirgends eine ausführbare Datei namens foobar finden. Wo befindet sich diese?
2.4.1.5. OK, ich habe eine ausführbare Datei namens foobar, ich kann sie sehen, wenn ich ls aufrufe. Gebe ich jedoch foobar in die Kommandozeile ein wird mir gesagt, daß eine Datei mit diesem Namen nicht existiert. Warum kann die Datei nicht gefunden werden?
2.4.1.6. Ich habe meine ausführbare Datei test genannt, allerdings passiert nichts wenn ich diese aufrufe. Was ist hier los?
2.4.1.7. Ich habe mein Programm kompiliert und bei dessen Aufruf sah zuerst alles gut aus. Jedoch gab es dann eine Fehlermeldung, welche irgendetwas mit “core dumped” lautete. Was bedeutet das?
2.4.1.8. Faszinierendes Zeugs, aber was soll ich jetzt machen?
2.4.1.9. Als mein Programm den core dump erzeugt hat, sagte es etwas von einem “segmentation fault”. Was ist das?
2.4.1.10. Wenn ich einen core dump erhalte erscheint manchmal die Meldung “bus error”. In meinem UNIX-Buch steht, daß die Ursache ein Hardwareproblem sei. Der Computer scheint aber weiterhin zu funktionieren. Ist dies wahr?
2.4.1.11. Diese Sache mit den core dumps hört sich sehr nützlich an, wenn ich so etwas selber an beliebiger Stelle bewirken könnte. Kann ich das tun, oder muß ich warten bis ein Fehler auftritt?

2.4.1.1. Ich versuche ein Programm zu schreiben, welches die Funktion sin() verwendet, erhalte jedoch eine Fehlermeldung. Was bedeutet diese?

/var/tmp/cc0143941.o: Undefined symbol `_sin' referenced from text segment
	     

Wenn Sie mathematische Funktionen wie sin() verwenden wollen, müssen Sie den cc anweisen, die Mathematik-Bibliothek wie folgt zu verlinken:

% cc -o foobar foobar.c -lm
	     

2.4.1.2. So, ich habe jetzt dieses einfache Programm als Übung für -lm geschrieben. Alles was es macht ist, 2.1 hoch 6 zu berechnen.

#include <stdio.h>

int main() {
	float f;

	f = pow(2.1, 6);
	printf("2.1 ^ 6 = %f\n", f);
	return 0;
}
	     

und ich habe es wie folgt kompiliert:

% cc temp.c -lm
	     

wie mir gesagt wurde. Allerdings bekomme ich jetzt bei der Ausführung die folgende Ausgabe:

% ./a.out
2.1 ^ 6 = 1023.000000
	     

Das ist nicht die richtige Antwort! Was ist hier los?

Wenn der Compiler Ihren Funktionsaufruf sieht, überprüft er, ob er schon einmal einen Prototypen für diese gesehen hat. Wenn nicht nimmt er als Rückgabewert den Typ int an, was sicherlich nicht das ist, was Sie an dieser Stelle wollen.

2.4.1.3. Wie kann ich das korrigieren?

Die Prototypen der mathematischen Funktionen befinden sich in der Datei math.h. Wenn Sie diese Datei in Ihrem Quelltext includen ist der Compiler in der Lage, den Prototypen zu finden, und wird aufhören, seltsame Dinge mit Ihrer Berechnung zu machen!

#include <math.h>
#include <stdio.h>

int main() {
...
	     

Nach erneutem Compilieren sollte das Folgende bei der Ausführung ausgegeben werden:

% ./a.out
2.1 ^ 6 = 85.766121
	     

Wenn Sie irgendwelche mathematischen Funktionen verwenden sollten Sie immer die Datei math.h includen und nicht vergessen, Ihr Programm gegen die Mathematik-Bibliothek zu verlinken.

2.4.1.4. Ich habe eine Datei mit dem Namen foobar.c kompiliert, kann jedoch nirgends eine ausführbare Datei namens foobar finden. Wo befindet sich diese?

Denken Sie daran, daß der cc die ausführbare Datei a.out nennt, wenn Sie nicht explizit einen Namen angeben. Verwenden Sie in solch einem Fall die Option -o filename:

% cc -o foobar foobar.c
	     

2.4.1.5. OK, ich habe eine ausführbare Datei namens foobar, ich kann sie sehen, wenn ich ls aufrufe. Gebe ich jedoch foobar in die Kommandozeile ein wird mir gesagt, daß eine Datei mit diesem Namen nicht existiert. Warum kann die Datei nicht gefunden werden?

Im Gegensatz zu MS-DOS sucht UNIX nicht im aktuellen Verzeichnis nach einem ausführbaren Programm, das Sie versuchen auszuführen, solange Sie dies nicht explizit mit angeben. Sie können entweder ./foobar eingeben, was soviel bedeutet wie “führe eine Datei namens foobar im aktuellen Verzeichnis aus”, oder Sie können Ihre Umgebungsvariable PATH so erweitern, daß sie ähnlich wie folgt aussieht

bin:/usr/bin:/usr/local/bin:.
	     

Der Punkt am Ende bedeutet “siehe im aktuellen Verzeichnis nach, wenn es in keinem der anderen zu finden war”.

2.4.1.6. Ich habe meine ausführbare Datei test genannt, allerdings passiert nichts wenn ich diese aufrufe. Was ist hier los?

Bei den meisten UNIX-Systeme existiert bereits ein Programm mit dem Namen test im Verzeichnis /usr/bin, und die Shell nimmt dieses, bevor sie im aktuellen Verzeichnis nachsieht. Sie können entweder den folgenden Befehl eingeben:

% ./test
	     

oder Sie können einen geeigneteren Namen für Ihr Programm wählen!

2.4.1.7. Ich habe mein Programm kompiliert und bei dessen Aufruf sah zuerst alles gut aus. Jedoch gab es dann eine Fehlermeldung, welche irgendetwas mit “core dumped” lautete. Was bedeutet das?

Der Name core dump stammt noch aus sehr frühen Zeiten von UNIX, als die Maschinen noch Kernspeicher zum Speichern von Daten verwendeten. Einfach ausgedrückt, wenn bei einem Programm unter bestimmen Bedingungen ein Fehler auftrat, hat das System den Inhalt des Kernspeichers auf der Festplatte in eine Datei namens core geschrieben, welche der Programmierer dann näher untersuchen konnte, um die Ursache des Fehlers herauszufinden.

2.4.1.8. Faszinierendes Zeugs, aber was soll ich jetzt machen?

Verwenden Sie den gdb, um das Speicherabbild zu untersuchen (siehe Abschnitt 2.6).

2.4.1.9. Als mein Programm den core dump erzeugt hat, sagte es etwas von einem “segmentation fault”. Was ist das?

Diese Meldung heißt im Prinzip, daß Ihr Programm eine illegale Operation mit dem Speicher durchführen wollte; UNIX wurde so entworfen, daß es das andere Programme und das Betriebssystem selbst vor wildgewordenen Programmen schützt.

Häufige Ursachen hierfür sind:

  • Der Versuch, einen NULL-Zeiger zu beschreiben, z.B.

    char *foo = NULL;
    strcpy(foo, "bang!");
    		
    
  • Einen Zeiger zu verwenden, welcher noch nicht initialisiert wurde, z.B.

    char *foo;
    strcpy(foo, "bang!");
    		
    

    Der Zeiger hat einen zufälligen Wert, welcher mit etwas Glück in einen Bereich des Speichers zeigt, der für Ihr Programm nicht verfügbar ist, und der Kernel bricht Ihr Programm ab, bevor es irgendwelchen Schaden anrichten kann. Wenn Sie Pech haben zeigt der Zeiger irgendwo mitten in Ihr eigenes Programm, und verändert dort ihre eigenen Datenstrukturen, was zu sehr seltsamen Fehlern Ihres Programmes führt.

  • Der Versuch, auf Daten außerhalb eines Arrays zuzugreifen, z.B.

    int bar[20];
    bar[27] = 6;
    		
    
  • Der Versuch, Daten in eine Speicherbereich zu schreiben, der nur lesbar ist, z.B.

    char *foo = "My string";
    strcpy(foo, "bang!");
    		
    

    UNIX-Compiler speichern häufig feste Zeichenketten wie "My string" in nur lesbaren Speicherbereichen ab.

  • Wenn man unerlaubte Operationen mit malloc() und free() ausführt, z.B.

    char bar[80];
    free(bar);
    		
    

    oder

    char *foo = malloc(27);
    free(foo);
    free(foo);
    		
    

Einzelne solcher Fehler führen zwar nicht immer zu einem Fehlverhalten des Programms, stellen jedoch immer eine falsche Verwendung dar. Manche Systeme und Compiler sind toleranter als andere, weshalb Programme auf dem einen System einwandfrei laufen, auf dem anderen System jedoch abstürzen.

2.4.1.10. Wenn ich einen core dump erhalte erscheint manchmal die Meldung “bus error”. In meinem UNIX-Buch steht, daß die Ursache ein Hardwareproblem sei. Der Computer scheint aber weiterhin zu funktionieren. Ist dies wahr?

Nein, glücklicherweise nicht (es sei denn Sie haben wirklich ein Hardwareproblem…). Üblicherweise ist dies ein Weg Ihnen mitzuteilen, daß Sie auf Speicher in einer Weise zugegriffen haben, in der Sie dies nicht tun sollten.

2.4.1.11. Diese Sache mit den core dumps hört sich sehr nützlich an, wenn ich so etwas selber an beliebiger Stelle bewirken könnte. Kann ich das tun, oder muß ich warten bis ein Fehler auftritt?

Ja, nehmen sie einfach eine andere Konsole oder XTerm und führen Sie

% ps
	   

aus, um die Prozess-ID Ihres Programms herauszufinden. Führen Sie anschließend

% kill -ABRT pid
	   

aus, wobei pid die Prozess-ID ist, die Sie vorher ermittelt haben.

Dies ist nützlich, wenn sich Ihr Programm z.B. in einer Endlosschleife verfangen hat. Sollte Ihr Programm das Signal SIGABRT abfangen, gibt es noch andere Möglichkeiten, die denselben Effekt haben.

Alternativ können Sie einen core dump aus Ihrem Programm heraus erstellen, indem Sie die Funktion abort() aufrufen. Weitere Informationen darüber können Sie in der Manualpage abort(3) nachlesen.

Wenn Sie einen core dump von außerhalb Ihres Programms erzeugen wollen, ohne dabei den Prozess abzubrechen, können Sie das Programm gcore verwenden. Weitere Informationen dazu finden Sie in der zugehörigen Manualpage gcore(1).

Fußnoten

[1]

Um genau zu sein übersetzt der cc den Quelltext an dieser Stelle nicht in Assemblersprache, sondern in seine eigene, maschinenunabhängige Sprache namens p-code.

[2]

Falls Sie es nicht wußten, Binary Sort ist, im Gegensatz zu Bubble Sort, eine effektive Möglichkeit, Dinge zu sortieren.

[3]

Der Grund dafür ist im Haufen der Geschichte begraben.

[4]

Beachten Sie, daß an dieser Stelle die Option -o zum Festlegen des Namens der ausführbaren Datei nicht verwendet wurde, weswegen an dieser Stelle die erzeugte Datei a.out heißt. Die Erzeugung einer Debug-Version namens foobar ist als Übung dem Leser überlassen!

Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an <[email protected]>.
Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an <[email protected]>.