Copyright © 1995 by O'Reilly/International Thomson Verlag

Bitte denken Sie daran: Sie dürfen zwar die Online-Version ausdrucken, aber diesen Druck nicht fotokopieren oder verkaufen.

Wünschen Sie mehr Informationen zu der gedruckten Version des Buches "Linux - Wegweiser zur Installation & Konfiguration", dann klicken Sie hier.


Kapitel 6

Programmieren mit gcc

Die Programmiersprache C ist diejenige, die bei der Entwicklung von UNIX-Software mit Abstand am häufigsten eingesetzt wird. Vielleicht liegt das daran, daß UNIX selbst ursprünglich in C entwickelt wurde -- C ist die »Muttersprache« von UNIX. Die C-Compiler unter UNIX haben schon immer die Standards für andere Sprachen und Hilfsmittel wie Linker, Debugger usw. gesetzt. Konventionen, die mit den ersten C-Compilern eingeführt wurden, haben sich in der gesamten UNIX-Programmierung weitgehend unverändert erhalten. Wer den C-Compiler kennt, der kennt auch UNIX selbst. Bevor wir allzusehr abheben, lassen Sie uns lieber auf die Details eingehen.

Der C-Compiler von GNU, gcc , ist einer der vielseitigsten und fortschrittlichsten aller verfügbaren Compiler. Anders als andere C-Compiler (z.B. die mit AT&T- oder BSD-Distributionen ausgelieferten oder solche von Drittherstellern) unterstützt gcc alle modernen C-Standards -- etwa ANSI-C -- sowie viele Erweiterungen, die nur in gcc zu finden sind. Glücklicherweise kann gcc trotzdem zu älteren C-Compilern und älteren Methoden der C-Programmierung kompatibel gemacht werden.

gcc ist gleichzeitig ein C++-Compiler. Für diejenigen unter Ihnen, die sich lieber in der undurchschaubaren objektorientierten Welt aufhalten, wird C++ mit allem Drum und Dran unterstützt -- einschließlich solcher Eigenschaften wie Methoden-Templates aus dem C++ von AT&T. Es stehen auch komplette Klassenbibliotheken für C++ (etwa die vielen Programmierern bekannte iostream ) zur Verfügung.

Wer sich gerne mit seltenen Dingen umgibt, findet in gcc auch Unterstützung für Objective-C, einen objektorientierten Ableger von C, der nie sehr populär wurde. Aber das ist noch längst nicht alles, wie wir sehen werden.

In diesem Abschnitt wollen wir zeigen, wie Sie mit gcc unter Linux Programme kompilieren und linken. Wir gehen davon aus, daß Sie mit der Programmierung in C/C++ vertraut sind, aber wir setzen nicht voraus, daß Sie bereits Erfahrungen mit der Programmierumgebung unter UNIX gemacht haben. Diese Erfahrung sollen Sie hier mit uns gewinnen.

Ein schneller Überblick

Bevor wir Ihnen auch die letzten Details des gcc selbst verraten, wollen wir ein einfaches Beispiel vorführen und alle Schritte zeigen, die Sie unternehmen, wenn Sie ein C-Programm auf einem UNIX-System kompilieren.

Lassen Sie uns von folgendem Programmcode ausgehen -- ein weiteres Exemplar des wohlbekannten »Hello, World!«-Programms:

#include <stdio.h>
int main() {
  (void)printf("Hello, World!\n");
  return 0; /* Just to be nice */
}

Um aus diesem Programm lebenden, ausführbaren Code zu machen, bedarf es mehrerer Schritte. Die meisten dieser Schritte lassen sich in einem gcc -Befehl zusammenfassen, aber auf die Details gehen wir erst weiter unten in diesem Kapitel ein.

Zunächst muß der gcc -Compiler aus diesem Quellcode eine Objektdatei erzeugen. In der Objektdatei steht im wesentlichen der Maschinencode, der diesem C-Code entspricht. Die Datei enthält Code, der den Stack für den main() -Aufruf einrichtet, die mysteriöse Funktion printf() aufruft sowie Code, der den Wert 0 zurückliefert.

Der nächste Schritt besteht darin, die Objektdatei zu binden (linken), um eine ausführbare Datei zu erhalten. Es fällt nicht schwer zu erraten, daß dieser Schritt vom Linker erledigt wird. Die Aufgabe des Linkers besteht darin, Objektdateien mit Code aus den Libraries (Bibliotheken) zu binden und eine ausführbare Datei zu erzeugen. Der Objektcode aus unserem Beispiel stellt noch kein ausführbares Programm dar; es muß vor allen Dingen der Code für printf() dazugebunden werden. Außerdem gehören noch diverse Initialisierungsroutinen zu einem kompletten Programm, die der normale Sterbliche nie zu sehen bekommt.

Wo kommt der Code für printf() her? Aus den Bibliotheken. Man kann nicht allzu lange über den gcc schreiben, ohne die Libraries zu erwähnen. Eine Library ist im wesentlichen eine Sammlung von vielen Objektdateien mit einem Index dazu. Wenn der Linker nach dem Code für printf() sucht, schaut er in den Indizes aller Bibliotheken nach, die er dazubinden soll. Der Linker findet die Objektdatei, in der die Funktion printf() enthalten ist, extrahiert diese Objektdatei (die ganze Datei, die noch viel mehr als nur die Funktion printf() enthalten kann), und bindet sie mit der ausführbaren Datei.

In Wirklichkeit sind die Dinge viel komplizierter. Wir haben bereits erwähnt, daß Linux zwei Arten von Bibliotheken kennt -- static (statisch) und shared (etwa: gemeinsam genutzt). Was wir soeben beschrieben haben, sind statische Bibliotheken -- Bibliotheken, aus denen der eigentliche Code der Subroutinen mit der ausführbaren Datei gebunden wird. Der Code für Routinen wie printf() kann allerdings ziemlich lang sein. Weil viele Programme häufig aufgerufene Routinen aus den Libraries benutzen, ist es sinnlos, daß jede ausführbare Datei eine eigene Kopie des Library-Codes enthalten soll. Hier kommen die Shared Libraries ins Spiel.

Bei der Benutzung von Shared Libraries ist der gesamte Code für die gemeinsam genutzten Routinen in einer einzigen Bibliothek auf der Festplatte enthalten -- der »Library-Image-Datei« (etwa: Library-Abbild). Wenn ein Programm mit einer Shared Library gebunden wird, bekommt die ausführbare Datei lediglich »Stub Code« (etwa: Stummel-Code) statt des eigentlichen Codes der Routinen mit. Dieser Stub Code teilt dem Programm-Lader (program loader) mit, an welcher Stelle der Platte er zur Laufzeit den Library-Code finden kann (in der Image-Datei). Wenn unser nettes »Hello, World!«-Programm ausgeführt wird, bemerkt der Programm-Lader also, daß das Programm mit einer Shared Library gebunden wurde. Er findet dann die Image-Datei und lädt den Code für die Bibliotheksroutinen (z.B. printf() ) zusammen mit dem eigentlichen Programmcode. Der Stub Code teilt dem Loader mit, wo er den Code für printf() innerhalb der Image-Datei finden kann.

Selbst dies ist noch eine stark vereinfachte Darstellung dessen, was wirklich passiert. Die Shared Libraries von Linux benutzen »Sprungtabellen« (jump tables); diese Tabellen machen es möglich, daß neue Bibliotheken eingebunden und ihr Inhalt verändert wird, ohne daß die ausführbaren Programme, die diese Bibliotheken benutzen, erneut gebunden werden müssen. Der Stub Code in der ausführbaren Datei sucht sich in der Bibliothek einen weiteren Referenzpunkt -- und zwar in der Sprungtabelle. Auf diese Weise können der Inhalt der Bibliothek und die zugehörige Sprungtabelle geändert werden, ohne daß der Stub Code in der ausführbaren Datei geändert werden muß.

Sie sollten sich von diesen ziemlich abstrakten Informationen nicht verwirren lassen. Wir werden weiter unten anhand eines Beispiels aus dem richtigen Leben zeigen, wie Sie Ihre Programme kompilieren, binden und debuggen. Das ist eigentlich recht einfach -- um viele Details kümmert sich der gcc -Compiler selbst. Trotzdem kann es nicht schaden, wenn man versteht, was hinter den Kulissen vorgeht.

Eigenschaften des Compilers gcc

gcc bietet mehr Möglichkeiten, als wir hier jemals aufzählen können. Weiter unten stellen wir einige davon vor; gleichzeitig verweisen wir die Neugierigen auf die Manual-Page und die Info-Seiten zum gcc , wo Sie ohne Zweifel einige interessante Dinge über diesen Compiler erfahren werden. Weiter unten in diesem Abschnitt geben wir Ihnen einen kurzen Überblick über die nützlichsten Eigenschaften von gcc mit auf den Weg. Mit Hilfe dieser Informationen sollten Sie in der Lage sein, selbst herauszufinden, wie Sie die vielen anderen Eigenschaften des Compilers zu Ihrem Vorteil nutzen können.

Zunächst einmal unterstützt gcc die moderne »Standard«-C-Syntax, wie sie im wesentlichen vom ANSI-C-Standard beschrieben wird. Die wichtigste Eigenschaft dieses Standards sind die Funktionsprototypen (function prototypes). Konkret bedeutet das: Wenn Sie eine Funktion foo() definieren, die ein int zurückgibt und die beiden Argumente a (vom Typ char *) und b (vom Typ double) akzeptiert, können Sie diese Funktion folgendermaßen definieren:

int foo(char *a, double b) {
  /* Ihr Code ... */
}

Im Gegensatz dazu sieht die alte Syntax für die Definition einer Funktion ohne Funktionsprototypen so aus:

int foo(a, b)
char *a;
double b;
{
  /* Ihr Code ... */
}

Auch diese Syntax wird von gcc unterstützt. Natürlich beschreibt ANSI-C viele andere Konventionen, aber die Funktionsprototypen fallen einem neuen Programmierer zuerst auf.

Wer mit dem C-Programmierstil vertraut ist, wie er in modernen Büchern (z.B. die zweite Auflage von Kernighan und Ritchies The C Programming Language ) gepflegt wird, sollte keine Probleme haben, mit dem gcc zu programmieren. (Die C-Compiler, die mit einigen anderen UNIX-Systemen ausgeliefert werden, unterstützen solche ANSI-Eigenschaften wie das Prototyping nicht.)

Der Compiler gcc kommt mit einem ziemlich beeindruckenden Optimierer (optimizer) daher. Während die meisten C-Compiler lediglich den Schalter -O kennen, um die Optimierung einzuschalten, unterstützt gcc etliche Stufen (level) der Optimierung. Auf der höchsten Optimierungsstufe zieht gcc solche Tricks aus dem Ärmel wie die gemeinsame Nutzung von Programmcode und statischen Daten. Das bedeutet: Wenn in Ihrem Programm eine feste Zeichenfolge wie »Hello, World!« auftaucht, und die ASCII-Darstellung dieser Zeichenfolge entspricht zufälligerweise einer Befehlsfolge in Ihrem Programm, dann wird gcc diesen Speicherplatz sowohl als Zeichenfolge als auch für den Programmcode nutzen. Wie schlau kann ein Compiler werden?

Selbstverständlich können Sie mit gcc auch Debugging-Informationen in die Objektdateien einbinden, so daß der Debugger (und damit der Programmierer) das Programm schrittweise abarbeiten kann. Der Compiler fügt dazu in die Objektdatei Markierungen ein, damit der Debugger im kompilierten Code bestimmte Zeilen, Variablen und Funktionen finden kann. Wenn Sie also mit einem Debugger wie gdb arbeiten (mit dem wir uns weiter unten in diesem Kapitel beschäftigen werden), haben Sie die Möglichkeit, das kompilierte Programm in kleinen Schritten ablaufen zu lassen und gleichzeitig den Quelltext zu verfolgen.

Einer der anderen Tricks, die der gcc beherrscht, ist seine Fähigkeit, Assemblercode zu erzeugen -- im wahrsten Sinne des Wortes durch das Setzen eines Schalters. Statt gcc Ihren Quelltext zu Maschinensprache kompilieren zu lassen, können Sie ihn anweisen, Assemblercode zu erzeugen, der für Menschen viel einfacher zu lesen ist. Das stellt auch eine bequeme Methode dar, unter Linux die Feinheiten der Assemblerprogrammierung für den Protected Mode zu erlernen -- schreiben Sie C-Code, lassen Sie gcc daraus Assemblercode erzeugen und studieren Sie diesen.

Falls Sie sich wundern, woher dieser Assemblercode kommt: gcc verfügt über einen eigenen Assembler (der unabhängig von gcc benutzt werden kann). Sie können auch Assemblercode in Ihre C-Quellen einfügen (inline assembler), falls Sie einmal besonders heftig tricksen müssen, aber nicht alles in Assembler schreiben möchten.

gcc-Grundlagen

Sicherlich wollen Sie jetzt endlich wissen, wie Sie alle diese wunderbaren Eigenschaften nutzen können. Besonders für neue UNIX- und C-Programmierer ist es wichtig zu lernen, wie sie gcc effektiv einsetzen. Die Arbeit mit einem Compiler, der von der Befehlszeile aus gesteuert wird, unterscheidet sich deutlich von der Arbeit mit einem Entwicklungssystem wie z.B. Borland-C unter MS-DOS. Zwar ist die Syntax der beiden Sprachen ähnlich, aber die Vorgehensweise beim Kompilieren und Binden von Programmen ist völlig anders.

Wie sollten Sie also vorgehen, wenn Sie unser harmlos aussehendes »Hello, World!«-Beispiel kompilieren und linken möchten?

Als erstes muß natürlich der Quellcode eingegeben werden. Dazu benutzen Sie einfach einen Texteditor wie Emacs oder vi . Der angehende Programmierer sollte den Quelltext eingeben und in einer Datei z.B. mit dem Namen hello.c speichern. (Wie die meisten C-Compiler ist auch gcc wählerisch, wenn es um die Dateinamensuffixe geht -- daran kann er ablesen, ob es sich um C-Quelltext, Assemblercode, eine Objektdatei usw. handelt.) Für Standard-C-Quelltexte sollten Sie das Suffix .c verwenden.

Um aus dem Quelltext das ausführbare Programm hello zu erzeugen, würde der Programmierer folgendes eingeben:

papaya$ gcc -o hello hello.c 

Vorausgesetzt, daß keine Fehler enthalten waren, würde gcc mit einem einzigen Streich aus dem Quellcode eine Objektdatei erzeugen, diese mit den entsprechenden Libraries binden und dann das lauffähige Programm hello ausspucken -- voila! Falls Sie das als vorsichtiger Programmierer testen möchten:

papaya$ hello 
Hello, World!
papaya$

Genau so freundlich, wie man das erwarten konnte.

Offensichtlich ist hinter den Kulissen eine ganze Menge passiert, als wir diesen einzelnen gcc -Befehl aufgerufen haben. Zunächst mußte gcc aus Ihrer Quelldatei hello.c die Objektdatei hello.o erzeugen. Anschließend mußte hello.o mit den Standard-Libraries gebunden sowie eine ausführbare Datei erzeugt werden.

gcc geht normalerweise davon aus, daß Sie nicht nur die angegebenen Quelldateien kompilieren wollen, sondern daß sie auch gebunden werden sollen (untereinander sowie mit den Standard-Libraries), um eine ausführbare Datei zu erzeugen. gcc kompiliert zuerst alle Quelldateien zu Objektdateien. Anschließend wird automatisch der Linker aufgerufen, um alle Objektdateien und Libraries zu einem ausführbaren Programm zusammenzufügen. (Richtig, der Linker ist ein eigenständiges Programm namens ld , der nicht zu gcc gehört -- man kann allerdings sagen, daß gcc und ld gute Freunde sind.) gcc kennt außerdem die »Standard«-Bibliotheken, die von den meisten Programmen benutzt werden, und weist ld an, diese einzubinden. Natürlich gibt es mehrere Methoden, solche Voreinstellungen zu ignorieren.

Sie haben die Möglichkeit, mit einem gcc -Befehl mehrere Dateinamen zu übergeben, aber in großen Projekten werden Sie wahrscheinlich jeweils nur wenige Dateien kompilieren und die Objektdateien ( .o ) aufbewahren. Wenn Sie aus einer Quelldatei nur die Objektdatei erzeugen wollen, ohne das Ganze auch binden zu lassen, können Sie in gcc den Schalter -c setzen:

papaya$ gcc -c hello.c

Damit erzeugen Sie die Objektdatei hello.o -- und sonst nichts.

Der Linker ist so voreingestellt, daß er eine ausführbare Datei ausgerechnet unter dem Namen a.out erzeugt. Mit dem Schalter -o können Sie gcc zwingen, die zu erzeugende Datei anders zu benennen; in diesem Fall hello . Es handelt sich hierbei lediglich um zurückgebliebenen Müll aus frühen UNIX-Versionen -- nichts besonders Aufregendes.

Mehrere Quelldateien benutzen

Der nächste Schritt auf Ihrem Pfad der Erleuchtung mit gcc besteht darin, daß Sie die Kompilierung mit mehreren Quelldateien verstehen. Nehmen wir an, daß Ihr Programm aus den beiden Quelldateien foo.c und bar.c besteht. Natürlich würden Sie eine oder mehrere Header-Dateien benutzen (etwa foo.h ), in denen Funktionsdeklarationen enthalten sind, die von beiden Programmen benutzt werden. Auf diese Weise kennt foo.c die Funktionen in bar.c und umgekehrt.

Geben Sie folgenden Befehl ein, um diese Quellcodedateien kompilieren und linken zu lassen (mit den Libraries natürlich), und daraus die ausführbare Datei baz zu erstellen:

papaya$ gcc -o baz foo.c bar.c

Dies entspricht etwa den drei Befehlen:

papaya$ gcc -c foo.c 
papaya$ gcc -c bar.c 
papaya$ gcc -o baz foo.o bar.o

gcc bildet also ein bequemes Front-End zum Linker und zu anderen »versteckten« Utilities, die während der Kompilierung aufgerufen werden.

Offensichtlich kann das Kompilieren eines Programms, das aus mehreren Quelldateien besteht, mit nur einem Befehl eine ganze Weile dauern. Wenn Ihr Programm z.B. aus fünf oder mehr Quelldateien besteht, würde der eben gezeigte gcc -Befehl vor dem Linken alle Quelldateien nacheinander noch einmal kompilieren. Das wäre eine große Zeitverschwendung -- insbesondere, wenn Sie nach der letzten Kompilierung nur eine Quelldatei geändert hatten. Die anderen Dateien bräuchten nicht noch einmal kompiliert zu werden, da ihre weiterhin gültigen Objektdateien noch vorhanden sind.

Die Lösung dieses Problems bieten Programme zur Projektverwaltung wie z.B. make , das wir im Abschnitt » Die Makefiles « weiter unten in diesem Kapitel besprechen werden.

Die Optimierung

Die Anweisung an gcc , den Code während der Kompilierung zu optimieren, ist recht einfach; benutzen Sie dazu in der Befehlszeile den Schalter -O :

papaya$ gcc -O -o fishsticks fishsticks.c

Etwas weiter oben haben wir erwähnt, daß gcc verschiedene Stufen der Optimierung kennt. Wenn Sie statt -O beispielsweise -O2 angeben, werden verschiedene »teure« Optimierungen eingeschaltet, die einerseits den Kompilierungsvorgang verlangsamen können, andererseits aber Ihren Code (hoffentlich) wesentlich effektiver machen.

Sie haben bei Ihren Streifzügen durch die Linux-Welt vielleicht bemerkt, daß einige Programme mit dem Schalter -O6 kompiliert werden (der Linux-Kernel ist ein gutes Beispiel dafür). Die aktuelle Version des gcc unterstützt die Optimierung auf der Stufe -O6 nicht, so daß hier (z.Zt.) mit der Stufe -O2 optimiert wird. Trotzdem wird gelegentlich -O6 angegeben, um Kompatibilität zu kommenden Versionen von gcc herzustellen; somit ist sichergestellt, daß in Zukunft die höchste Optimierungsstufe benutzt wird.

Den Code debuggen

Der Schalter -g in gcc fügt Debugging-Code in die kompilierten Objektdateien ein. Das bedeutet, daß sowohl in die Objektdatei als auch in die entstehende ausführbare Datei zusätzliche Informationen eingefügt werden. Diese Informationen ermöglichen es, mit einem Debugger wie gdb (keine Bange, wir kommen weiter unten in diesem Kapitel darauf zu sprechen) das Programm schrittweise zu durchlaufen. Der Nachteil beim Einbinden von Debugging-Code ist, daß die Größe der entstehenden Objektdateien erheblich zunimmt. Außerdem können Programme mit Debugging-Code nur mit statischen Bibliotheken gebunden werden, was die Binärdateien weiter anschwellen läßt. In der Regel ist es sinnvoll, -g nur zu benutzen, solange Sie Ihre Programme entwickeln und testen; bei der »endgültigen« Kompilierung sollten Sie diesen Schalter nicht setzen.

Glücklicherweise schließen sich Debugging-Code und Optimierung nicht gegenseitig aus. Sie können also ohne weiteres den Befehl:

papaya$ gcc -O -g -o mumble mumble.c

aufrufen. Allerdings kann es vorkommen, daß nach bestimmten Optimierungen mit -O oder -O2 das Programm sich anscheinend unberechenbar verhält, wenn es vom Debugger unter die Fittiche genommen wird. In der Regel empfiehlt es sich, entweder -O oder -g zu setzen, aber nicht beide.

Mehr Spaß mit Libraries

Bevor wir die Gefilde von gcc verlassen, wollen wir noch ein paar Worte zum Thema Linken und Libraries sagen. Es ist gar nicht so schwierig, eigene Bibliotheken zu erzeugen. Wenn Sie eine Reihe von Routinen geschrieben haben, die Sie häufig aufrufen, sollten Sie diese vielleicht zu einer Gruppe von Quelltextdateien zusammenfassen, aus jeder Quelldatei eine Objektdatei erzeugen, und dann aus diesen Objektdateien eine Bibliothek erstellen. Anschließend brauchen Sie diese Routinen nicht mit jedem Programm, das darauf zugreift, erneut zu kompilieren.

Nehmen wir an, daß Sie einige Quelldateien haben, in denen häufig benutzte Routinen wie z.B.:

float square(float x) {
  /* Code für square()... */
}

int factorial(int x, int n) {
  /* Code für factorial()... */
}

usw. stehen. (Natürlich sind in den Standardbibliotheken von gcc solche gebräuchlichen Routinen bereits enthalten; lassen Sie sich also durch dieses Beispiel nicht verwirren.) Der Einfachheit halber wollen wir weiterhin annehmen, daß der Code für square() in der Datei square.c steht und der Code für factorial() in factorial.c .

Um eine Bibliothek zu erzeugen, die diese Routinen enthält, müssen Sie nur diese Quelldateien kompilieren:

papaya$ gcc -c square.c factorial.c

Damit erhalten Sie square.o und factorial.o . Als nächstes erzeugen Sie aus diesen Objektdateien die Bibliothek. Dabei zeigt sich, daß eine Bibliothek einfach eine Archivdatei ist, die mit dem Befehl ar erzeugt wird (einem engen Verwandten von tar ). Wir wollen unsere Bibliothek libstuff.a nennen, und so gehen wir vor:

papaya$ ar r libstuff.a square.o factorial.o

Wenn Sie eine solche Bibliothek aktualisieren möchten, sollten Sie vielleicht vorher die alte libstuff.a löschen. Als letzten Schritt erstellen wir einen Index zu dieser Bibliothek, damit der Linker die Routinen darin finden kann. Benutzen Sie dazu den Befehl ranlib , etwa so:

papaya$ ranlib libstuff.a

Dieser Befehl fügt zusätzliche Informationen in die Bibliothek selbst ein; es wird keine eigenständige Indexdatei erzeugt.

Mit libstuff.a haben Sie jetzt eine statische Library, die ihre Routinen enthält. Bevor Sie Programme mit dieser Bibliothek binden können, müssen Sie noch eine Header-Datei schreiben, die den Inhalt der Bibliothek beschreibt. Wir könnten z.B. die Datei libstuff.h mit folgendem Inhalt erstellen:

/* libstuff.h: routines in libstuff.a */
extern float square(float);
extern int factorial(int, int);

In jede Quellcodedatei, die Routinen aus libstuff.a aufruft, sollten Sie die Zeile #include "libstuff.h" einfügen, wie Sie das mit den Standard-Header-Dateien auch tun würden.

Wie kompilieren wir Programme, die auf die soeben fertiggestellte Bibliothek samt Header-Datei zugreifen? Zunächst müssen wir beide an einer Stelle abspeichern, wo der Compiler sie finden kann. Viele Programmierer legen eigene Bibliotheken im Unterverzeichnis lib ihres Home-Verzeichnisses ab und eigene Include-Dateien unter include .

Wir gehen davon aus, daß dies bereits geschehen ist, und können dann das ominöse Programm wibble.c mit folgendem Befehl kompilieren:

papaya$ gcc -I../include -L../lib -o wibble wibble.c -lstuff

Mit der Option -I weisen Sie gcc an, das Verzeichnis ../include in den Include -Pfad einzufügen, in dem gcc Include-Dateien sucht. Die Option -L funktioniert ganz ähnlich, indem sie gcc anweist, das Verzeichnis ../lib in den Library -Pfad einzutragen.

Das letzte Argument auf der Befehlszeile ist -lstuff ; damit wird der Linker angewiesen, die Bibliothek libstuff.a einzubinden (solange sie irgendwo im Library-Pfad zu finden ist). Das lib am Anfang des Dateinamens wird für Bibliotheken automatisch angenommen.

Sie sollten den Schalter -l auf der Befehlszeile jedesmal benutzen, wenn Sie andere als die Standardbibliotheken einbinden wollen. Wenn Sie beispielsweise mathematische Routinen aus math.h benutzen möchten, sollten Sie am Ende der gcc -Befehlszeile -lm anhängen, womit libm eingebunden wird. Bedenken Sie aber, daß die Reihenfolge der -l -Optionen von Bedeutung ist. Ein Beispiel: Wenn Ihre Bibliothek libstuff Routinen aus libm aufruft, dann muß in der Befehlszeile -lm hinter <option>-lstuff</option> stehen:

papaya$ gcc -I../include -L../lib -o wibble wibble.c -lstuff -lm

Damit zwingen Sie den Linker, libm nach libstuff zu binden; dabei können die noch nicht aufgelösten Verweise in libstuff bearbeitet werden.

Wo sucht gcc nach Bibliotheken? Per Voreinstellung werden die Bibliotheken in verschiedenen Verzeichnissen gesucht; das wichtigste davon ist /usr/lib . Wenn Sie einen Blick auf den Inhalt von /usr/lib werfen, werden Sie feststellen, daß dort eine ganze Reihe von Library-Dateien abgelegt sind -- einige der Dateinamen enden auf .a , andere auf .sa . Dabei verbergen sich hinter den .a -Dateien die statischen Bibliotheken, z.B. auch unsere libstuff.a . Die .sa -Dateien sind die Shared-Library-Stubs -- sie enthalten nur den »Stub Code«, der zum dynamischen Binden während der Laufzeit für solche Programme benötigt wird, die Shared Libraries benutzen.

Wir können uns schon vorstellen, was Sie als nächstes fragen werden: Wo steht der eigentliche Code der Shared Libraries? Der Programm-Lader sucht zur Laufzeit an verschiedenen Stellen nach den Shared-Library-Images, u.a. auch in /lib . Wenn Sie in /lib nachschauen, werden Sie Dateien wie z.B. libc.so.4.4.4 vorfinden. Dies ist die Image-Datei, die den Code für die Shared Library libc enthält (eine der Standardbibliotheken, die zu den meisten Programmen gebunden wird). Die Datei /usr/lib/libc.sa enthält die Stubs für diese Bibliothek.

Der Linker ist so voreingestellt, daß er versucht, Shared Libraries einzubinden. Es gibt allerdings auch Situationen, in denen die statischen Bibliotheken benutzt werden. Wenn Sie z.B. mit -g Debugging-Code erzeugen, werden die statischen Bibliotheken eingebunden. Sie können außerdem mit dem Schalter -static von gcc das Einbinden von statischen Bibliotheken ausdrücklich anfordern.

Programmieren mit C++

Für den Fall, daß Sie lieber objektorientiert programmieren, bietet gcc die vollständige Unterstützung sowohl für C++ als auch für Objective-C. Es gibt nur wenige Dinge, die Sie bedenken müssen, wenn Sie mit dem gcc in C++ programmieren möchten.

Zunächst sollten die Namen der C++-Quelldateien auf .C oder .cc enden. Damit werden sie von den Dateien mit einfachem C-Code unterschieden, deren Namen auf .c enden.

Zweitens sollten Sie den Shell-Befehl g++ statt gcc benutzen, wenn Sie C++-Code kompilieren. g++ ruft den gcc mit einigen zusätzlichen Parametern auf, z.B. um die C++-Standardbibliotheken einzubinden. g++ akzeptiert dieselben Argumente und Optionen wie gcc .

Wenn Sie g++ nicht aufrufen, aber C++-Klassen wie die E/A-Objekte cout und cin benutzen möchten, müssen Sie sicherstellen, daß die C++-Bibliotheken eingebunden werden. Vergewissern Sie sich auch, daß die C++-Bibliotheken und Include-Dateien tatsächlich installiert sind; einige Distributionen enthalten nur die Standard-C-Libraries. Während gcc Ihre C++-Programme noch problemlos kompiliert, bekommen Sie ohne die C++-Bibliotheken jedesmal Fehlermeldungen vom Linker, wenn Sie versuchen, die Standardobjekte zu benutzen.


Inhaltsverzeichnis Vorherige Abschnitt Nächste Abschnitt