Kapitel 1. Richte dich für eine einfache Zusammenstellung ein
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Pass auf, Schatz, denn ich benutze Technologie.
Iggy Pop, "Search and Destroy"
Die C-Standardbibliothek reicht einfach nicht aus, um ernsthafte Arbeit zu erledigen.
Stattdessen hat sich das C-Ökosystem über den Standard hinaus ausgedehnt, was bedeutet, dass du wissen musst, wie du Funktionen aus gängigen, aber nicht zum ISO-Standard gehörenden Bibliotheken einfach aufrufen kannst, wenn du über die Lehrbuchübungen hinauskommen willst. Wenn du mit einer XML-Datei, einem JPEG-Bild oder einer TIFF-Datei arbeiten willst, brauchst du libxml, libjpeg oder libtiff, die alle frei erhältlich, aber nicht Teil des Standards sind. Leider ist das der Punkt, an dem die meisten Lehrbücher aufhören und dich allein lassen. Deshalb gibt es C-Verächter, die selbstgefällige Dinge sagen wie "C ist 40 Jahre alt, also musst du alles von Grund auf neu schreiben" - siehaben nie herausgefunden, wie man mit einer Bibliothek verknüpft.
Hier ist die Tagesordnung für das Kapitel:
- Richte die erforderlichen Werkzeuge ein
- Das ist viel einfacher als in den dunklen Tagen, als du jedes einzelne Bauteil zusammensuchen musstest. Du kannst ein komplettes System mit allem Drum und Dran in vielleicht 10 oder 15 Minuten einrichten (plus die Zeit für den Download, um so viel gutes Material zu laden).
- Kompiliere ein C-Programm
- Ja, du weißt, wie man das macht, aber wir brauchen ein Setup, das Haken für die Bibliotheken und ihre Standorte hat; einfach
cc
myfile.c
reicht nicht mehr aus. Make ist so ziemlich das einfachste System, um das Kompilieren von Programmen zu erleichtern, daher bietet es ein gutes Modell für die Diskussion. Ich zeige dir das kleinstmögliche Makefile, das genug Platz zum Wachsen bietet. - Variablen einrichten und neue Bibliotheken hinzufügen
- Welches System wir auch immer verwenden, es wird auf einer kleinen Anzahl von Umgebungsvariablen basieren, deshalb werde ich erklären, was sie tun und wie man sie setzt. Wenn wir diese Kompilierungsmechanismen erst einmal eingerichtet haben, können wir ganz einfach neue Bibliotheken hinzufügen, indem wir die Variablen anpassen, die wir bereits eingerichtet haben.
- Ein Kompilierungssystem einrichten
- Als Bonus können wir alles, was wir bis hierher gelernt haben, nutzen, um ein noch einfacheres System für die Kompilierung einzurichten, mit dem wir Code ausschneiden und in die Eingabeaufforderung einfügen können.
Ein besonderer Hinweis für IDE-Benutzer: Du bist vielleicht kein make
Benutzer, aber dieser Abschnitt ist trotzdem für dich relevant, denn für jedes Rezept, das make
beim Kompilieren von Code ausführt, hat deine IDE ein entsprechendes Rezept. Wenn du weißt, was make
macht, wird es dir leicht fallen, deine IDE zu optimieren.
Einen Paketmanager verwenden
Wenn du keinen Paketmanager verwendest, verpasst du etwas.
Ich erwähne Paketmanager aus mehreren Gründen: Erstens haben einige von euch die Grundlagen vielleicht noch nicht installiert. Für dich habe ich diesen Abschnitt an den Anfang des Buches gestellt, denn du brauchst diese Tools, und zwar schnell. Mit einem guten Paketmanager hast du ziemlich schnell ein komplettes POSIX-Subsystem, Compiler für jede Sprache, von der du je gehört hast, eine halbwegs anständige Auswahl an Spielen, die üblichen Office-Tools, ein paar hundert C-Bibliotheken und so weiter.
Zweitens ist der Paketmanager für uns als C-Autoren ein wichtiges Mittel, um Bibliotheken in unsere Arbeit einzubinden.
Drittens: Wenn du den Sprung von jemandem, der Pakete herunterlädt, zu jemandem, der ein Paket erstellt, schaffst, wird dich dieses Buch auf halbem Weg begleiten. Es zeigt dir, wie du dein Paket für eine einfache automatische Installation vorbereitest, so dass der Administrator eines Paket-Repositorys, der deinen Code in das Repository aufnehmen möchte, keine Probleme hat, das endgültige Paket zu erstellen.
Wenn du ein Linux-Benutzer bist, hast du deinen Computer mit einem Paketmanager eingerichtet und bereits gesehen, wie einfach der Softwarebeschaffungsprozess sein kann. Für Windows-Nutzer werde ich weiter unten Cygwin vorstellen. Mac-Benutzer haben mehrere Möglichkeiten, z. B. Fink, Homebrew und Macports. Alle Mac-Optionen hängen von Apples Xcode-Paket ab, das (je nach Jahrgang deines Macs) kostenlos über die OS-Installations-CD, das Verzeichnis der installierbaren Programme, den Apple App Store oder durch die Registrierung als Entwickler bei Apple erhältlich ist.
Welche Pakete wirst du brauchen? Hier findest du eine kurze Übersicht über die Grundlagen der C-Entwicklung. Da jedes System anders organisiert ist, können einige dieser Pakete unterschiedlich gebündelt sein, standardmäßig in einem Basispaket installiert werden oder einen seltsamen Namen haben. Im Zweifelsfall solltest du ein Paket installieren, denn die Zeiten, in denen die Installation von zu vielen Dingen das System instabil machte oder verlangsamte, sind vorbei. Allerdings hast du wahrscheinlich nicht die Bandbreite (oder vielleicht sogar den Speicherplatz), um alle angebotenen Pakete zu installieren, also musst du abwägen. Wenn du feststellst, dass dir etwas fehlt, kannst du es später immer noch nachholen. Pakete, die du unbedingt haben solltest:
-
Ein Compiler. Installiere auf jeden Fall
gcc
;clang
kann verfügbar sein. -
GDB, ein Debugger.
-
Valgrind, um auf Fehler bei der Speichernutzung von C zu testen.
-
gprof
einen Profiler. -
make
so dass du deinen Compiler nie direkt aufrufen musst. -
pkg-config
für die Suche nach Bibliotheken. -
Doxygen, für die Erstellung der Dokumentation.
-
Ein Texteditor. Es gibt buchstäblich Hunderte von Texteditoren, aus denen du wählen kannst. Hier sind ein paar subjektive Empfehlungen:
-
Emacs und vim sind die Favoriten der Hardcore-Geeks. Emacs ist sehr umfassend (das E steht für extensible); vim ist eher minimalistisch und sehr freundlich zu Touch-Tippern. Wenn du damit rechnest, Hunderte von Stunden vor einem Texteditor zu verbringen, lohnt es sich, einen der beiden zu lernen.
-
Kate ist freundlich und attraktiv und bietet einen guten Teil der Annehmlichkeiten, die wir als Programmierer erwarten, wie z.B. die Syntaxhervorhebung.
-
Als letzten Ausweg kannst du nano ausprobieren, das sehr einfach und textbasiert ist und daher auch dann funktioniert, wenn dein GUI nicht funktioniert.
-
-
Wenn du ein Fan von IDEs bist, besorg dir eine - oder mehrere. Auch hier gibt es eine große Auswahl; hier sind ein paar Empfehlungen:
-
Anjuta: gehört zur GNOME-Familie. Befreundet mit Glade, dem GNOME GUI-Builder.
-
KDevelop: gehört zur KDE-Familie.
-
XCode: Die IDE von Apple für OS X.
-
Code::blocks: relativ einfach, funktioniert unter Windows.
-
Eclipse: das Luxusauto mit vielen Getränkehaltern und zusätzlichen Knöpfen. Auch plattformübergreifend.
-
In späteren Kapiteln werde ich mich mit diesen schwereren Werkzeugen befassen:
-
Autotools: Autoconf, Automake, libtool
-
Git
-
Alternative Schalen, wie z.B. die Z-Schale.
Und natürlich gibt es die C-Bibliotheken, die es dir ersparen, das Rad neu zu erfinden (oder, um es metaphorisch auszudrücken, die Lokomotive neu zu erfinden). Vielleicht möchtest du noch mehr, aber hier sind die Bibliotheken, die im Laufe dieses Buches verwendet werden:
-
libcURL
-
libGLib
-
libGSL
-
libSQLite3
-
libXML2
Es gibt keinen Konsens über die Namensgebung von Bibliothekspaketen und du musst herausfinden, wie dein Paketmanager eine einzelne Bibliothek in Teilpakete zerlegen möchte. In der Regel gibt es ein Paket für die Benutzer und ein zweites für die Autoren, die die Bibliothek in ihrer eigenen Arbeit verwenden werden. Achte also darauf, sowohl das Basispaket als auch die Pakete -dev
oder -devel
auszuwählen. Bei manchen Systemen wird die Dokumentation in ein weiteres Paket ausgelagert. In diesem Fall sollte GDB dich durch die Schritte führen, wenn du es zum ersten Mal auf einem System ohne Debugsymbole ausführst.
Wenn du ein POSIX-System verwendest, hast du nach der Installation der vorangegangenen Elemente ein komplettes Entwicklungssystem und kannst mit dem Programmieren beginnen. Für Windows-Benutzer machen wir einen kurzen Abstecher, um zu verstehen, wie das Setup mit dem Windows-Hauptsystem zusammenspielt.
C mit Windows kompilieren
Auf den meisten Systemen ist C die zentrale VIP-Sprache, die alle anderen Tools unterstützt; auf einem Windows-System wird C seltsamerweise ignoriert.
Deshalb muss ich kurz darauf eingehen, wie man eine Windows-Box für das Schreiben von Code in C einrichtet. Wenn du jetzt nicht auf einer Windows-Box schreibst, kannst du diesen Abschnitt überspringen und zu "Welcher Weg zur Bibliothek?" springen .
POSIX für Windows
Da sich C und Unix gemeinsam entwickelt haben, ist es schwierig, über das eine und nicht über das andere zu sprechen. Ich denke, es ist einfacher, mit POSIX zu beginnen. Auch für diejenigen unter euch, die versuchen, Code auf einem Windows-Rechner zu kompilieren, den ihr anderswo geschrieben habt, ist dies der natürlichste Weg.
Soweit ich das beurteilen kann, teilt sich die Welt der Dinge mit Dateisystemen in zwei leicht überlappende Klassen:
-
POSIX-kompatible Systeme
-
Die Windows-Familie der Betriebssysteme
POSIX-Konformität bedeutet nicht, dass ein System aussehen und sich anfühlen muss wie eine Unix-Box. Der typische Mac-Benutzer hat zum Beispiel keine Ahnung, dass er oder sie ein Standard-BSD-System mit einem attraktiven Frontend benutzt, aber wer sich auskennt, kann in den Ordner Dienstprogramme (im Ordner Programme) gehen, dann das Terminal-Programm öffnen und ls
, grep
und make
nach Herzenslust ausführen.
Außerdem bezweifle ich, dass viele Systeme 100 % der Anforderungen des Standards erfüllen (z. B. einen Fortran '77-Compiler). Für unsere Zwecke brauchen wir eine Shell, die sich wie eine einfache POSIX-Shell verhält, eine Handvoll Dienstprogramme (sed, grep, make usw.), einen C99-Compiler und Ergänzungen zur Standard-C-Bibliothek wie fork
und iconv
. Diese können dem Hauptsystem als Nebenbemerkung hinzugefügt werden. Die zugrundeliegenden Skripte des Paketmanagers, die Autotools und fast jeder andere Versuch, portabel zu programmieren, stützen sich bis zu einem gewissen Grad auf diese Werkzeuge. Auch wenn du nicht den ganzen Tag auf eine Eingabeaufforderung starren willst, sind diese Werkzeuge bei Installationen sehr nützlich.
Auf Betriebssystemen der Serverklasse und den Vollversionen von Windows 7 bietet Microsoft das Subsystem für Unix-basierte Anwendungen (SUA) an, das früher INTERIX hieß und heute die üblichen POSIX-Systemaufrufe, die Korn-Shell und gcc
bereitstellt. Das Subsystem wird normalerweise nicht standardmäßig bereitgestellt, sondern kann als Zusatzkomponente installiert werden. Das SUA ist jedoch nicht für andere aktuelle Windows-Editionen verfügbar und wird auch nicht für Windows 8 verfügbar sein. Wir können uns also nicht darauf verlassen, dass Microsoft ein POSIX-Subsystem für seine Betriebssysteme bereitstellt.
Wenn du Cygwin von Grund auf neu bauen würdest, wäre das dein Programm:
-
Schreibe eine C-Bibliothek für Windows, die alle POSIX-Funktionen bereitstellt. Sie muss einige Unstimmigkeiten zwischen Windows und POSIX ausgleichen, z. B. dass Windows verschiedene Laufwerke wie C: hat, während POSIX ein einheitliches Dateisystem hat. In diesem Fall kannst du C: als /cygdrive/c, D: als /cygdrive/d und so weiter bezeichnen.
-
Jetzt, wo du POSIX-Standard-Programme kompilieren kannst, indem du auf deine Bibliothek linkst, kannst du das tun: Erzeuge Windows-Versionen von
ls
,bash
,grep
,make
,gcc
,X
,rxvt
,libglib
,perl
,python
, und so weiter. -
Wenn du Hunderte von Programmen und Bibliotheken erstellt hast, richte einen Paketmanager ein, mit dem die Benutzer die Elemente auswählen können, die sie installieren möchten.
Als Cygwin-Benutzer musst du nur den Paketmanager von dem Setup-Link auf der Cygwin-Website herunterladen und Pakete auswählen. Natürlich brauchst du die oben genannte Liste und ein vernünftiges Terminal (versuch es mit mintty oder installiere das X-Subsystem und benutze das xterm, denn beide sind viel freundlicher als das von Windows cmd.exe
), aber du wirst sehen, dass praktisch alle Annehmlichkeiten, die du von einem Entwicklungssystem kennst, irgendwo vorhanden sind.
In "Pfade" bespreche ich verschiedene Umgebungsvariablen, die sich auf die Kompilierung auswirken, darunter auch Pfade für die Suche nach Dateien. Das gilt nicht nur für POSIX: Auch Windows hat Umgebungsvariablen, die du in den Systemeinstellungen in der Systemsteuerung findest. Cygwin ist viel benutzerfreundlicher, wenn du sein bin
Verzeichnis (wahrscheinlich c:\cygwin\bin
) zum Windows PATH
hinzufügst.
Jetzt kannst du mit dem Kompilieren von C-Code beginnen.
C mit POSIX kompilieren
Microsoft bietet mit Visual Studio einen C++-Compiler an, der über einen C89-Kompatibilitätsmodus verfügt (gemeinhin als ANSI C bezeichnet, auch wenn C11 der aktuelle ANSI-Standard ist). Dies ist die einzige Möglichkeit, C-Code zu kompilieren, die Microsoft derzeit anbietet. Viele Vertreter des Unternehmens haben deutlich gemacht, dass über die Unterstützung einiger C99-Funktionen hinaus (geschweige denn die Unterstützung von C11) keine weiteren Entwicklungen zu erwarten sind. Visual Studio ist der einzige große Compiler, der noch an C89 festhält, so dass wir uns anderweitig nach Alternativen umsehen müssen.
Natürlich bietet Cygwin gcc
an, und wenn du Cygwin installiert hast, hast du bereits eine vollständige Build-Umgebung.
Standardmäßig hängen Programme, die du unter Cygwin kompilierst, von der Bibliothek der POSIX-Funktionen, cygwin1.dll, ab (unabhängig davon, ob dein Code POSIX-Aufrufe enthält oder nicht). Wenn du dein Programm auf einem Rechner ausführst, auf dem Cygwin installiert ist, hast du kein Problem. Die Benutzer können auf die ausführbare Datei klicken und sie wie erwartet ausführen, da das System die Cygwin-DLL finden sollte. Ein unter Cygwin kompiliertes Programm kann auch auf Rechnern laufen, auf denen Cygwin nicht installiert ist, wenn du cygwin1.dll mit deinem Code verteilst. Auf meinem Rechner ist der Pfad zu Cygwin folgendermaßen: /bin/cygwin1.dll. Die Datei cygwin1.dll hat eine GPL-ähnliche Lizenz (siehe "Die rechtliche Seite"), d.h. wenn du die DLL getrennt von Cygwin als Ganzes weitergibst, musst du den Quellcode für dein Programm veröffentlichen.1
Wenn das ein Problem ist, musst du einen Weg finden, dein Programm neu zu kompilieren, ohne von cygwin1.dll abhängig zu sein. Das bedeutet, dass du alle POSIX-spezifischen Funktionen (wie fork
oder popen
) aus deinem Code streichen und MinGW verwenden musst, wie später beschrieben. Mit cygcheck
kannst du herausfinden, von welchen DLLs dein Programm abhängt, und so überprüfen, ob deine ausführbare Datei mit cygwin1.dll verknüpft ist oder nicht.
Hinweis
Um zu sehen, von welchen anderen Bibliotheken ein Programm oder eine dynamisch gelinkte Bibliothek abhängt:
-
Cygwin:
cygcheck
libxx
.dll -
Linux:
ldd
libxx
.so -
Mac:
otool -L
libxx
.dylib
C ohne POSIX kompilieren
Wenn dein Programm die POSIX-Funktionen nicht benötigt, kannst du MinGW (Minimalist GNU for Windows) verwenden, das einen Standard-C-Compiler und einige grundlegende Werkzeuge zur Verfügung stellt. MSYS ist eine Ergänzung zu MinGW, die eine Shell und andere nützliche Hilfsprogramme bietet.
MSYS bietet eine POSIX-Shell (und du kannst die Terminals mintty oder RXVT finden, in denen du deine Shell ausführen kannst), oder du lässt die Eingabeaufforderung ganz hinter dir und versuchst es mit Code::blocks, einer IDE, die MinGW zur Kompilierung unter Windows verwendet. Eclipse ist eine weitaus umfangreichere IDE, die ebenfalls für MinGW konfiguriert werden kann, allerdings ist dafür etwas mehr Einarbeitungszeit erforderlich.
Oder wenn du dich an einer POSIX-Eingabeaufforderung wohler fühlst, dann richte Cygwin trotzdem ein, besorge dir die Pakete, die die MinGW-Versionen von gcc
bereitstellen, und benutze diese zum Kompilieren anstelle der POSIX-verbindenden Standardversion von Cygwin gcc
.
Wenn du die Autotools noch nicht kennst, wirst du sie bald kennenlernen. Ein Paket, das mit den Autotools erstellt wurde, zeichnet sich dadurch aus, dass es mit drei Befehlen installiert wird: ./configure && make && make install
. MSYS bietet genügend Möglichkeiten, damit solche Pakete eine gute Chance haben, zu funktionieren. Wenn du die Pakete heruntergeladen hast, um sie über die Eingabeaufforderung von Cygwin zu bauen, kannst du das Paket auch so einrichten, dass es den Mingw32-Compiler von Cygwin verwendet, um POSIX-freien Code zu erzeugen:
.
/
configure
--
host
=
mingw32
Dann führe make && make install
wie gewohnt aus.
Sobald du unter MinGW kompiliert hast, entweder über die Befehlszeile oder mit den Autotools, hast du eine native Windows-Binärdatei. Da MinGW nichts von cygwin1.dll weiß und dein Programm sowieso keine POSIX-Aufrufe macht, hast du jetzt ein ausführbares Programm, das ein echtes Windows-Programm ist und von dem niemand weiß, dass du es in einer POSIX-Umgebung kompiliert hast.
Allerdings gibt es in MinGW derzeit nur wenige vorkompilierte Bibliotheken.2 Wenn du frei von cygwin1.dll sein willst, kannst du nicht die Version von libglib.dll verwenden, die mit Cygwin geliefert wird. Du musst GLib aus dem Quellcode in eine native Windows-DLL umkompilieren - aber GLib hängt von GNUs gettext
für die Internationalisierung ab, also musst du diese Bibliothek zuerst bauen. Moderner Code hängt von modernen Bibliotheken ab, daher kann es sein, dass du viel Zeit damit verbringst, Dinge einzurichten, die in anderen Systemen mit einem einzeiligen Aufruf des Paketmanagers erledigt sind. Damit sind wir wieder bei den Dingen, die die Leute dazu bringen, darüber zu reden, dass C 40 Jahre alt ist und man alles von Grund auf neu schreiben muss.
Das sind also die Vorbehalte. Microsoft hat sich aus der Diskussion zurückgezogen und überlässt es anderen, einen Post-Grunge-C-Compiler und eine entsprechende Umgebung zu entwickeln. Cygwin tut dies und bietet einen vollständigen Paketmanager mit genügend Bibliotheken, um einen Teil oder die gesamte Arbeit zu erledigen, aber es ist mit einem POSIX-Schreibstil und der DLL von Cygwin verbunden. Wenn das ein Problem ist, musst du mehr Arbeit investieren, um die Umgebung und die Bibliotheken zu erstellen, die du brauchst, um anständigen Code zu schreiben.
Welcher Weg zur Bibliothek?
OK, du hast also einen Compiler, eine POSIX-Toolchain und einen Paketmanager, der problemlos ein paar hundert Bibliotheken installieren kann. Jetzt können wir uns dem Problem zuwenden, diese beim Kompilieren unserer Programme zu verwenden.
Wir müssen mit der Compiler-Befehlszeile beginnen, die schnell zu einem Durcheinander wird, aber wir werden mit drei (manchmal dreieinhalb) relativ einfachen Schritten enden:
-
Setzt eine Variable, die die Compiler-Flags auflistet.
-
Setze eine Variable, die die Bibliotheken auflistet, mit denen gelinkt werden soll. Der halbe Schritt ist, dass du manchmal nur eine Variable für das Linken beim Kompilieren setzen musst und manchmal zwei für das Linken zur Kompilier- und zur Laufzeit.
-
Richte ein System ein, das diese Variablen verwendet, um die Kompilierung zu steuern.
Um eine Bibliothek zu verwenden, musst du dem Compiler zweimal sagen, dass du Funktionen aus der Bibliothek importieren wirst: einmal für die Kompilierung und einmal für den Linker. Bei einer Bibliothek an einem Standardort erfolgen die beiden Deklarationen über ein #include
im Text des Programms und ein -l
Flag in der Compilerzeile.
Beispiel 1-1 zeigt ein schnelles Beispielprogramm, das ein paar amüsante mathematische Berechnungen durchführt (zumindest für mich; wenn der statistische Jargon für dich griechisch ist, ist das okay). Die C99-Standardfehlerfunktion erf(x)
ist eng verwandt mit dem Integral von Null bis x der Normalverteilung mit Mittelwert Null und Standardabweichung √2. Hier verwenden wir erf
, um einen unter Statistikern beliebten Bereich zu überprüfen (das 95%-Konfidenzintervall für einen standardmäßigen large-n-Hypothesentest ). Wir nennen diese Datei erf.c.
Beispiel 1-1. Ein Einzeiler aus der Standardbibliothek. (erf.c)
#include <math.h>
//erf, sqrt
#include <stdio.h>
//printf
int
main
(){
printf
(
"The integral of a Normal(0, 1) distribution "
"between -1.96 and 1.96 is: %g
\n
"
,
erf
(
1.96
*
sqrt
(
1
/
2.
)));
}
Die Zeilen #include
sollten dir bekannt vorkommen. Der Compiler fügt hier math.h und stdio.h in die Codedatei ein und somit auch die Deklarationen für printf
, erf
und sqrt
. Die Deklaration in math.h sagt nichts darüber aus, was erf
tut, sondern nur, dass sie eine double
aufnimmt und eine double
zurückgibt. Das ist genug Information für den Compiler, um die Konsistenz unserer Verwendung zu prüfen und eine Objektdatei mit einem Hinweis zu erzeugen, der dem Computer sagt: "Wenn du zu diesem Hinweis kommst, suche die Funktion erf
und ersetze diesen Hinweis durch den Rückgabewert von erf
."
Es ist die Aufgabe des Linkers, diese Notiz auszugleichen, indem er erf
findet, die sich in einer Bibliothek irgendwo auf deiner Festplatte befindet.
Die mathematischen Funktionen in math.h sind in eine eigene Bibliothek ausgelagert, die du dem Linker mit dem Flag -lm
mitteilen musst. Hier ist -l
das Flag, das anzeigt, dass eine Bibliothek eingebunden werden muss, und die Bibliothek hat in diesem Fall einen Namen mit nur einem Buchstaben, m
. Du bekommst printf
umsonst, denn es gibt eine implizite -lc
, die den Linker auffordert, die standardmäßig angenommene libc
am Ende des Link-Befehls zu linken. Später werden wir sehen, dass GLib 2.0 über -lglib-2.0
eingebunden wird, die GNU Scientific Library wird über -lgsl
eingebunden und so weiter.
Wenn die Datei erf.c hieße, sähe die vollständige Befehlszeile mit dem Compiler gcc
einschließlich einiger zusätzlicher Flags, die wir gleich besprechen werden, wie folgt aus:
gcc erf.c -o erf -lm -g -Wall -O3 -std=gnu11
Wir haben also dem Compiler gesagt, dass er die mathematischen Funktionen über #include
in das Programm einbinden soll, und wir haben dem Linker gesagt, dass er mit der mathematischen Bibliothek über -lm
auf der Kommandozeile linken soll.
Das Flag -o
gibt den Namen der Ausgabe an; andernfalls würden wir den Standardnamen der ausführbaren Datei a.out erhalten.
Ein paar meiner Lieblingsflaggen
Du wirst sehen, dass ich jedes Mal ein paar Compiler-Flags verwende, und ich empfehle dir, das auch zu tun.
-
-g
fügt Symbole für die Fehlersuche hinzu. Ohne sie ist dein Debugger nicht in der Lage, dir Variablen- oder Funktionsnamen zu geben. Sie verlangsamen das Programm nicht und es ist uns egal, ob das Programm ein Kilobyte größer ist, also gibt es wenig Grund, dies nicht zu verwenden. Es funktioniert fürgcc
,clang
undicc
(Intel C Compiler). -
-std=gnu11
istclang
- undgcc
-spezifisch und legt fest, dass der Compiler Code zulassen soll, der den C11- und POSIX-Standards (sowie einigen GNU-Erweiterungen) entspricht. Ab diesem Zeitpunkt verwendetclang
standardmäßig den C11-Standard undgcc
den C89-Standard. Wenn deine Kopie vongcc
,clang
odericc
älter als C11 ist, benutze-std=gnu99
um den C99-Standard zu verwenden. Der POSIX-Standard schreibt vor, dassc99
auf deinem System vorhanden sein muss. Die compiler-agnostische Version der obigen Zeile für die Kompilierung von C99-Code wäre also:c99 erf.c -o erf -lm -g -Wall -O3
In den folgenden Makefiles erreiche ich diesen Effekt, indem ich die Variable
CC=c99
setze.Warnung
Je nach Jahrgang deines Macs kann
c99
eine speziell gehackte Version vongcc
sein, die du wahrscheinlich nicht willst. Wenn du eine Version vonc99
hast, die bei der Flagge-Wall
aufhört oder ganz fehlt, kannst du deine eigene Version erstellen. Lege ein Bash-Skript mit dem Namenc99
in das Verzeichnis am Anfang deines Pfades mit dem folgenden Text:gcc --std=gnu99 $*
oder
clang $*
wie du willst. Mach sie über
chmod +x c99
ausführbar. -
-O3
steht für die Optimierungsstufe drei, die alle Tricks ausprobiert, um schnelleren Code zu erstellen. Wenn du beim Ausführen des Debuggers feststellst, dass zu viele Variablen optimiert wurden, so dass du nicht mehr nachvollziehen kannst, was vor sich geht, dann ändere dies in-O0
. Dies wird später eine übliche Änderung der VariableCFLAGS
sein. Das funktioniert auch fürgcc
,clang
undicc
. -
-Wall
fügt Compiler-Warnungen hinzu. Das funktioniert fürgcc
,clang
undicc
. Füricc
solltest du-w1
bevorzugen, das die Warnungen des Compilers anzeigt, aber nicht seine Anmerkungen.
Warnung
Nutze immer die Warnungen deines Compilers. Du bist vielleicht anspruchsvoll und kennst den C-Standard in- und auswendig, aber du bist nicht anspruchsvoller oder kenntnisreicher als dein Compiler. In alten C-Lehrbüchern wirst du seitenweise ermahnt, auf den Unterschied zwischen =
und ==
zu achten oder zu überprüfen, ob alle Variablen vor der Verwendung initialisiert wurden. Als moderner Lehrbuchautor habe ich es leicht, denn ich kann all diese Ermahnungen in einem einzigen Tipp zusammenfassen: Verwende immer die Warnungen deines Compilers.
Wenn dein Compiler zu einer Änderung rät, solltest du ihn nicht anzweifeln oder die Korrektur aufschieben. Tu alles, was nötig ist, um (1) zu verstehen, warum du eine Warnung erhalten hast, und (2) deinen Code so zu korrigieren, dass er ohne Warnungen und ohne Fehler kompiliert werden kann. Compiler-Meldungen sind bekanntermaßen schwer zu verstehen. Wenn du also Probleme mit Schritt (1) hast, gib die Warnmeldung in deine Internet-Suchmaschine ein, um herauszufinden, wie viele andere vor dir von dieser Warnung verwirrt wurden. Du solltest vielleicht noch -Werror
zu deinen Compiler-Flags hinzufügen, damit dein Compiler Warnungen als Fehler behandelt.
Pfade
Ich habe über 700.000 Dateien auf meiner Festplatte, und eine davon enthält die Deklarationen für sqrt
und erf
, und eine andere ist die Objektdatei mit den kompilierten Funktionen.3 Der Compiler muss wissen, in welchen Verzeichnissen er suchen muss, um die richtige Header- und Objektdatei zu finden, und das Problem wird noch komplizierter, wenn wir Bibliotheken verwenden, die nicht Teil des C-Standards sind.
In einer typischen Einrichtung gibt es mindestens drei Orte, an denen Bibliotheken installiert werden können:
-
Der Hersteller des Betriebssystems kann ein oder zwei Standardverzeichnisse definieren, in denen die Bibliotheken vom Hersteller installiert werden.
-
Es kann ein Verzeichnis geben, in dem der lokale Systemadministrator Pakete installiert, die bei der nächsten Aktualisierung des Betriebssystems durch den Hersteller nicht überschrieben werden sollen. Zum Beispiel könnte der Sysadmin eine speziell gehackte Version einer Bibliothek haben, die die Standardversion überschreiben soll.
-
Benutzer haben in der Regel keine Schreibrechte für diese Speicherorte und sollten daher die Bibliotheken in ihren Heimatverzeichnissen verwenden können.
Die Standardverzeichnisse des Betriebssystems bereiten in der Regel keine Probleme und der Compiler sollte wissen, dass er in diesen Verzeichnissen die C-Standardbibliothek und alles, was daneben installiert ist, finden muss. Im POSIX-Standard werden diese Verzeichnisse als "die üblichen Orte" bezeichnet.
Aber für die anderen Sachen musst du dem Compiler sagen, wo er suchen soll. Das wird kompliziert: Es gibt keinen Standardweg, um Bibliotheken an nicht standardmäßigen Orten zu finden, und das steht ganz oben auf der Liste der Dinge, die die Leute an C frustrieren. Auf der einen Seite weiß dein Compiler, wie er an den üblichen Orten suchen muss, und die Bibliotheksanbieter neigen dazu, die Dinge an den üblichen Orten abzulegen, sodass du vielleicht nie einen Pfad manuell angeben musst. Ein weiterer Pluspunkt ist, dass es einige Tools gibt, die dir bei der Angabe von Pfaden helfen. Ein weiterer Pluspunkt ist, dass du die nicht standardmäßigen Pfade in einer Shell- oder Makefile-Variable festlegen kannst, sobald du sie auf deinem System gefunden hast, und nie wieder darüber nachdenken musst.
Angenommen, du hast eine Bibliothek namens Libuseful auf deinem Computer installiert und weißt, dass ihre verschiedenen Dateien im Verzeichnis /usr/local/ abgelegt wurden, das offiziell für die lokalen Bibliotheken deines Sysadmins vorgesehen ist. Du hast #include <useful.h>
bereits in deinen Code eingefügt; jetzt musst du dies in die Befehlszeile eingeben:
gcc -I/usr/local/include use_useful.c -o use_useful -L/usr/local/lib -luseful
-
-I
fügt den angegebenen Pfad zum Include-Suchpfad hinzu, den der Compiler nach Header-Dateien durchsucht, die du#include
d in deinem Code hast. -
-L
zum Suchpfad der Bibliothek hinzufügt. -
Die Reihenfolge ist wichtig. Wenn du eine Datei namens specific.o hast, die von der Libbroad-Bibliothek abhängt, und Libbroad von Libgeneral, dann brauchst du:
gcc specific.o -lbroad -lgeneral
Jede andere Reihenfolge, wie z. B.
gcc -lbroad -lgeneral specific.o
, wird wahrscheinlich fehlschlagen. Du kannst dir vorstellen, dass der Linker sich das erste Element,specific.o
, ansieht und eine Liste der ungelösten Funktions-, Struktur- und Variablennamen aufschreibt. Dann geht er zum nächsten Eintrag,-lbroad
, und sucht nach den Einträgen auf der noch fehlenden Liste, wobei er möglicherweise neue ungelöste Einträge hinzufügt, und überprüft dann-lgeneral
auf die Einträge, die noch auf der fehlenden Liste stehen. Wenn am Ende der Liste noch Namen unauffindbar sind (einschließlich der impliziten-lc
am Ende), hält der Linker an und gibt den Rest der Liste der fehlenden Elemente an den Benutzer weiter.
OK, zurück zum Speicherortproblem: Wo befindet sich die Bibliothek, auf die du verlinken willst? Wenn sie über denselben Paketmanager installiert wurde, mit dem du auch den Rest deines Betriebssystems installiert hast, befindet sie sich höchstwahrscheinlich an den üblichen Orten und du musst dir keine Sorgen machen.
Du hast vielleicht ein Gefühl dafür, wo sich deine eigenen lokalen Bibliotheken befinden, z. B. /usr/local, /sw oder /opt. Sicherlich hast du eine Möglichkeit, die Festplatte zu durchsuchen, z. B. ein Suchprogramm auf deinem Desktop oder das POSIX:
find /usr -name 'libuseful*'
um /usr nach Dateien zu durchsuchen, deren Namen mit libuseful beginnen. Wenn du feststellst, dass die Shared Object-Datei von Libuseful in /some/path/lib liegt, befinden sich die Header mit ziemlicher Sicherheit in /some/path/include.
Alle anderen finden es auch lästig, auf der Festplatte nach Bibliotheken zu suchen. pkg-config
kümmert sich darum, indem es ein Repository mit den Flags und Speicherorten verwaltet, die die Pakete selbst als notwendig für die Kompilierung melden. Gib pkg-config
in die Befehlszeile ein. Wenn du eine Fehlermeldung über die Angabe von Paketnamen erhältst, hast du pkg-config
und kannst damit die Suche für dich erledigen. Auf meinem PC gebe ich zum Beispiel diese beiden Befehle in die Befehlszeile ein:
pkg-config --libs gsl libxml-2.0 pkg-config --cflags gsl libxml-2.0
gibt mir diese beiden Zeilen aus:
-lgsl -lgslcblas -lm -lxml2 -I/usr/include/libxml2
Das sind genau die Flags, die ich brauche, um mit GSL und LibXML2 zu kompilieren. Die -l
Flaggen zeigen, dass die GNU Scientific Library von einer BLAS-Bibliothek (Basic Linear Algebra Subprograms) abhängt und die BLAS-Bibliothek der GSL von der Standard-Mathe-Bibliothek. Es scheint, dass sich alle Bibliotheken an den üblichen Stellen befinden, denn es gibt keine -L
Flaggen, aber die -I
Flagge gibt den Ort für die Header-Dateien von LibXML2 an.
Zurück in der Kommandozeile: Wenn du einen Befehl mit Backticks umgibst, ersetzt die Shell den Befehl durch seine Ausgabe. Das heißt, wenn ich tippe:
gcc `pkg-config --cflags --libs gsl libxml-2.0` -o specific specific.c
der Compiler sieht:
gcc -I/usr/include/libxml2 -lgsl -lgslcblas -lm -lxml2 -o specific specific.c
pkg-config
nimmt uns also viel Arbeit ab, aber es ist nicht so standardisiert, dass wir erwarten können, dass jeder es hat oder dass jede Bibliothek damit registriert ist. Wenn du pkg-config
nicht hast, musst du das selbst herausfinden, indem du das Handbuch deiner Bibliothek liest oder deine Festplatte durchsuchst, wie wir bereits gesehen haben.
Warnung
Oft gibt es Umgebungsvariablen für Pfade, wie CPATH
oder LIBRARY_PATH
oder C_INCLUDE_PATH
. Du würdest sie in deiner .bashrc
oder einer anderen benutzerspezifischen Liste von Umgebungsvariablen setzen. Sie sind hoffnungslos uneinheitlich -gcc
unter Linux und gcc
auf dem Mac verwenden unterschiedliche Variablen, und jeder andere Compiler kann noch andere verwenden. Meiner Meinung nach ist es einfacher, diese Pfade projektspezifisch im Makefile oder einer entsprechenden Datei zu setzen, indem du die Flags -I
und -L
verwendest. Wenn du diese Pfadvariablen bevorzugst, findest du am Ende der Manpage deines Compilers eine Liste der Variablen, die für deine Situation relevant sind.
Selbst mit pkg-config
wird der Bedarf an etwas, das all das für uns zusammensetzt, immer deutlicher. Jedes Element ist leicht zu verstehen, aber es ist eine lange, mechanische Liste von mühsamen Teilen.
Laufzeitverknüpfung
Statische Bibliotheken werden vom Compiler gelinkt, indem der Inhalt der Bibliothek in die endgültige ausführbare Datei kopiert wird. Das Programm selbst funktioniert also mehr oder weniger als eigenständiges System. Gemeinsam genutzte Bibliotheken werden zur Laufzeit mit deinem Programm verknüpft, was bedeutet, dass wir zur Laufzeit das gleiche Problem haben, die Bibliothek zu finden, das wir zur Kompilierzeit hatten. Noch schlimmer ist, dass auch die Benutzer deines Programms dieses Problem haben können.
Wenn sich die Bibliothek in einem der üblichen Pfade befindet, ist alles in Ordnung und das System wird die Bibliothek zur Laufzeit problemlos finden. Wenn sich deine Bibliothek in einem nicht standardmäßigen Pfad befindet, musst du einen Weg finden, den Suchpfad für Bibliotheken zur Laufzeit zu ändern. Optionen:
-
Wenn du dein Programm mit Autotools gepackt hast, weiß Libtool, wie man die richtigen Flags hinzufügt, und du musst dich nicht darum kümmern.
-
Wenn du das Programm mit
gcc
,clang
odericc
basierend auf einer Bibliothek in libpath kompilierst, füge hinzu:LDADD=-L
libpath
-Wl,-Rlibpath
an das nachfolgende Makefile weiter. Das
-L
Flag teilt dem Compiler mit, wo er nach Bibliotheken suchen soll, um Symbole aufzulösen; das-Wl
Flag gibt seine Flags vongcc
/clang
/icc
an den Linker weiter, und der Linker bettet das angegebene-R
in den Laufzeit-Suchpfad für Bibliotheken ein, mit denen gelinkt werden soll. Leider kenntpkg-config
die Laufzeitpfade oft nicht, so dass du diese Dinge eventuell manuell eingeben musst. -
Zur Laufzeit verwendet der Linker einen weiteren Pfad, um Bibliotheken zu finden, die sich nicht an den üblichen Stellen befinden und nicht über
-Wl,R...
in einer ausführbaren Datei vermerkt sind. Dieser Pfad kann im Startskript deiner Shell festgelegt werden (.bashrc
,.zshrc
, oder wie auch immer es aussieht). Um sicherzustellen, dass libpath zur Laufzeit nach Shared Libraries durchsucht wird, verwende:export LD_LIBRARY_PATH=
libpath
:$LD_LIBRARY_PATH #Linux, Cygwin export DYLD_LIBRARY_PATH=libpath
:$DYLD_LIBRARY_PATH #OS XManche warnen vor einer übermäßigen Nutzung von
LD_LIBRARY_PATH
(was ist, wenn jemand eine böswillige Fälscherbibliothek in den Pfad einfügt und damit die echte Bibliothek ohne dein Wissen ersetzt?
Makefiles verwenden
Das Makefile bietet eine Lösung für all diese endlosen Anpassungen. Es ist im Grunde ein organisierter Satz von Variablen und Abfolgen von einzeiligen Shell-Skripten. Das Programm make
, das dem POSIX-Standard entspricht, liest das Makefile nach Anweisungen und Variablen und setzt dann die langen und mühsamen Befehlszeilen für uns zusammen. Nach diesem Abschnitt gibt es kaum noch einen Grund, den Compiler direkt aufzurufen.
In "Makefiles vs. Shell-Skripte" werde ich ein paar mehr Details über das Makefile behandeln; hier zeige ich dir das kleinste praktikable Makefile, das ein einfaches Programm kompiliert, das von einer Bibliothek abhängt. Hier ist es, alle sechs Zeilen davon:
P=program_name
OBJECTS=
CFLAGS = -g -Wall -O3
LDLIBS=
CC=c99
$(P): $(OBJECTS)
Verwendung:
-
Einmalig: Speichere sie (unter dem Namen makefile) im gleichen Verzeichnis wie deine .c-Dateien. Wenn du GNU Make verwendest, hast du die Möglichkeit, den Namen Makefile groß zu schreiben, wenn du meinst, dass er sich dadurch von den anderen Dateien abhebt. Setze den Namen deines Programms in die erste Zeile (verwende
progname
, nichtprogname
.c). -
Jedes Mal, wenn du neu kompilieren musst: Gib ein.
make
.
Variablen einstellen
Wir werden uns bald mit der eigentlichen Funktionsweise des Makefiles befassen, aber fünf von sechs Zeilen dieses Makefiles befassen sich mit dem Setzen von Variablen (von denen zwei derzeit leer sind), was darauf hindeutet, dass wir uns einen Moment Zeit nehmen sollten, um die Umgebungsvariablen ein wenig genauer zu betrachten.
Warnung
Historisch gesehen gab es zwei Hauptströmungen der Shell-Grammatik: eine, die hauptsächlich auf der Bourne-Shell basiert, und eine andere, die hauptsächlich auf der C-Shell basiert. Die C-Shell hat eine etwas andere Syntax für Variablen, z. B. set CFLAGS="-g -Wall -O3”
, um den Wert von CFLAGS
zu setzen. Der POSIX-Standard basiert jedoch auf der Bourne-Syntax für das Setzen von Variablen, weshalb ich mich im Rest dieses Buches auf diese Syntax konzentriere.
Die Shell und make
verwenden die $
, um den Wert einer Variablen anzugeben, aber die Shell verwendet $
var
, während make
alle Variablennamen, die länger als ein Zeichen sind, in Klammern setzt: $(
var
)
. In der vorangegangenen makefile wird $(P): $(OBJECTS)
also so ausgewertet, dass es bedeutet
program_name
:
Es gibt mehrere Möglichkeiten, make
über eine Variable zu informieren:
-
Setze die Variable von der Shell aus, bevor du
make
aufrufst, und exportiere die Variable, so dass die Shell, wenn sie einen Kindprozess startet, die Variable in ihrer Liste der Umgebungsvariablen hat. So setzen SieCFLAGS
von einer POSIX-Standard-Befehlszeile aus:export CFLAGS='-g -Wall -O3'
Zu Hause lasse ich die erste Zeile in diesem Makefile weg,
P=
program_name
und setze sie stattdessen einmal pro Sitzung überexport P=
program_name
Das bedeutet, dass ich das Makefile selbst noch seltener bearbeiten muss. -
Du kannst diese Exportbefehle in das Startskript deiner Shell einfügen, z. B.
.bashrc
oder.zshrc
. So ist gewährleistet, dass die Variable jedes Mal, wenn du dich anmeldest oder eine neue Shell startest, gesetzt und exportiert wird. Wenn du sicher bist, dass deineCFLAGS
jedes Mal die gleiche ist, kannst du sie hier setzen und musst nie wieder darüber nachdenken. -
Du kannst eine Variable für einen einzelnen Befehl exportieren, indem du die Zuweisung direkt vor den Befehl setzt. Der Befehl
env
listet die ihm bekannten Umgebungsvariablen auf, wenn du also Folgendes ausführstPANTS=kakhi env | grep PANTS
solltest du die entsprechende Variable und ihren Wert sehen. Das ist der Grund, warum die Shell keine Leerzeichen um das Gleichheitszeichen herum zulässt: Das Leerzeichen dient der Unterscheidung zwischen der Zuweisung und dem Befehl.
Mit diesem Formular werden die angegebenen Variablen nur für eine Zeile gesetzt und exportiert. Nachdem du dies in der Befehlszeile ausprobiert hast, führe
env | grep PANTS
erneut aus, um zu überprüfen, dassPANTS
keine exportierte Variable mehr ist.Du kannst so viele Variablen angeben, wie du möchtest:
PANTS=kakhi PLANTS="ficus fern" env | grep 'P.*NTS'
Diese Form ist Teil der einfachen Befehlsbeschreibung der Shell-Spezifikation, was bedeutet, dass die Zuweisung vor einem tatsächlichen Befehl stehen muss. Das ist wichtig, wenn wir uns mit Shell-Konstrukten beschäftigen, die keine Befehle sind. Schreiben:
VAR=val if [ -e afile ] ; then ./program_using_VAR ; fi
wird mit einem obskuren Syntaxfehler fehlschlagen. Die richtige Form ist:
if [ -e afile ] ; then VAR=val ./program_using_VAR ; fi
-
Wie in dem früheren Makefile kannst du die Variable am Anfang des Makefiles setzen, mit Zeilen wie
CFLAGS=
. Im Makefile kannst du Leerzeichen um das Gleichheitszeichen herum verwenden, ohne dass etwas kaputt geht. -
make
ermöglicht es dir, Variablen auf der Kommandozeile zu setzen, unabhängig von der Shell. Diese beiden Zeilen sind also fast gleichwertig:make CFLAGS="-g -Wall" Set a makefile variable. CFLAGS="-g -Wall" make Set an environment variable visible to make and its children.
Alle diese Mittel sind gleichwertig, soweit es dein Makefile betrifft, mit der Ausnahme, dass Kindprogramme, die von make
aufgerufen werden, neue Umgebungsvariablen kennen, aber keine Makefile-Variablen kennen.
make
bietet auch einige eingebaute Variablen. Hier sind die (POSIX-Standard-)Variablen, die du zum Lesen der folgenden Regeln benötigst:
$@
-
Der vollständige Name der Zieldatei. Mit Ziel ist die Datei gemeint, die gebaut werden muss, z. B. eine .o-Datei, die aus einer .c-Datei kompiliert wird, oder ein Programm, das durch die Verknüpfung von .o-Dateien entsteht.
$*
-
Die Zieldatei mit abgeschnittenem Suffix. Wenn die Zieldatei also prog.o ist, ist
$*
prog und$*.c
würde zu prog.c. $<
-
Der Name der Datei, die dieses Ziel ausgelöst und erstellt hat. Wenn wir prog.o erstellen, liegt das wahrscheinlich daran, dass prog.c kürzlich geändert wurde, also ist
$<
prog.c.
Die Regeln
Konzentrieren wir uns jetzt auf die Prozeduren, die das Makefile ausführt, und gehen wir dann darauf ein, wie die Variablen dies beeinflussen.
Abgesehen von den Variablen haben die Abschnitte des Makefiles die Form:
target
:dependencies
script
Wenn das Ziel über den Befehl make
aufgerufen wird. target
aufgerufen wird, werden die Abhängigkeiten überprüft. Wenn das Ziel eine Datei ist, die Abhängigkeiten alle Dateien sind und das Ziel neuer ist als die Abhängigkeiten, dann ist die Datei aktuell und es gibt nichts zu tun. Andernfalls wird die Verarbeitung des Ziels aufgeschoben, die Abhängigkeiten werden ausgeführt oder generiert, wahrscheinlich über ein anderes Ziel, und wenn die Abhängigkeitsskripte alle fertig sind, wird das Skript des Ziels ausgeführt.
Bevor dies ein Buch wurde, war es zum Beispiel eine Reihe von Tipps, die in einem Blog veröffentlicht wurden. Für jeden Blogbeitrag gab es eine HTML- und eine PDF-Version, die alle mit LaTeX erstellt wurden. Um ein einfaches Beispiel zu geben, lasse ich viele Details weg (z. B. die vielen Optionen für latex2html
), aber hier ist die Art von Makefile, die man für den Prozess schreiben könnte.
Warnung
Wenn du einen dieser Makefile-Schnipsel von einer Version auf deinem Bildschirm oder auf Papier in eine Datei mit dem Namen makefile kopierst, vergiss nicht, dass das Leerzeichen am Anfang jeder Zeile ein Tabulator sein muss, keine Leerzeichen. Schuld daran ist POSIX.
all: html doc publish doc: pdflatex $(f).tex html: latex -interaction batchmode $(f) latex2html $(f).tex publish: scp $(f).pdf $(Blogserver)
Ich setze f
in der Befehlszeile mit einem Befehl wie export f=tip-make
. Wenn ich dann make
in die Befehlszeile eingebe, wird das erste Ziel, all
, überprüft. Das bedeutet, dass der Befehl make
selbst dem Befehl make
entspricht. first_target
. Das hängt von html
, doc
und publish
ab, also werden diese Ziele nacheinander aufgerufen. Wenn ich weiß, dass es noch nicht bereit ist, in die Welt zu kopieren, dann kann ich make html doc
aufrufen und nur diese Schritte ausführen.
In dem einfachen Makefile von vorhin hatten wir nur eine Ziel-/Abhängigkeits-/Skriptgruppe. Zum Beispiel:
P=domath OBJECTS=addition.o subtraction.o $(P): $(OBJECTS)
Es folgt eine Abfolge von Abhängigkeiten und Skripten, ähnlich wie bei meinem Blogging-Makefile, aber die Skripte sind implizit. Hier ist P=domath
das Programm, das kompiliert werden soll, und es hängt von den Objektdateien addition.o und subtraction.o ab. Da addition.o nicht als Ziel aufgeführt ist, verwendet make
eine implizite Regel, die unten aufgeführt ist, um von der .c- zur .o-Datei zu kompilieren. Dann wird das Gleiche für subtraction.o und domath.o gemacht (weil GNU make
implizit davon ausgeht, dass domath
von domath.o abhängt, wenn man das hier berücksichtigt). Wenn alle Objekte gebaut sind, haben wir kein Skript, um das Ziel $(P)
zu bauen, also füllt GNU make
sein Standardskript zum Linken von .o-Dateien in eine ausführbare Datei aus.
Der POSIX-Standard make
hat dieses Rezept für die Kompilierung einer .o-Objektdatei aus einer .c-Quellcodedatei:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c
Die Variable $(CC)
steht für deinen C-Compiler; der POSIX-Standard gibt CC=c99
als Standard vor, aber aktuelle Ausgaben von GNU make
setzen CC=cc
, was normalerweise ein Link zu gcc
ist. Im minimalen Makefile am Anfang dieses Abschnitts wird $(CC)
explizit auf c99
gesetzt, $(CFLAGS)
auf die Liste der Flags von vorhin, und $(LDFLAGS)
ist nicht gesetzt und wird daher durch nichts ersetzt. Wenn make
also feststellt, dass your_program
.o erzeugt werden muss, wird dieser Befehl mit dem Makefile ausgeführt:
c99 -g -Wall -O3 -oyour_program
.oyour_program
.c
Wenn GNU make
entscheidet, dass du ein ausführbares Programm aus den Objektdateien bauen willst, verwendet es dieses Rezept:
$(CC) $(LDFLAGS)first.o
second.o
$(LDLIBS)
Erinnere dich daran, dass die Reihenfolge im Linker wichtig ist, deshalb brauchen wir zwei Linker-Variablen. Im vorherigen Beispiel brauchten wir:
cc specific.o -lbroad -lgeneral
als den relevanten Teil des Linking-Befehls. Wenn wir den korrekten Kompilierbefehl mit dem Rezept vergleichen, sehen wir, dass wir LDLIBS=-lbroad -lgeneral
setzen müssen.
Hinweis
Wenn du die vollständige Liste der Standardregeln und -variablen sehen möchtest, die in deiner Version von make
enthalten sind, probiere es aus:
make -p > default_rules
Das ist also das Spiel: Finde die richtigen Variablen und setze sie im Makefile. Du musst zwar immer noch recherchieren, was die richtigen Flags sind, aber zumindest kannst du sie im Makefile aufschreiben und musst nie wieder darüber nachdenken.
Wenn du eine IDE, CMAKE oder eine der anderen Alternativen zum POSIX-Standard make
verwendest, wirst du das gleiche Spiel mit dem Finden der Variablen spielen. Ich werde die Diskussion über das vorangegangene minimale Makefile fortsetzen, und du solltest kein Problem haben, die entsprechenden Variablen in deiner IDE zu finden.
-
Die Variable
CFLAGS
ist eine fest eingebaute Gewohnheit, aber die Variable, die du für den Linker setzen musst, ändert sich von System zu System. AuchLDLIBS
ist nicht der POSIX-Standard, aber es ist das, was GNUmake
verwendet. -
In die Variablen
CFLAGS
undLDLIBS
werden wir alle Compiler-Flags zum Auffinden und Identifizieren von Bibliotheken einfügen. Wenn dupkg-config
hast, füge die Backticked-Aufrufe hier ein. Das Makefile auf meinem System, in dem ich Apophenia und GLib für so ziemlich alles verwende, sieht zum Beispiel so aus:CFLAGS=`pkg-config --cflags apophenia glib-2.0` -g -Wall -std=gnu11 -O3 LDLIBS=`pkg-config --libs apophenia glib-2.0`
Oder du gibst die Flags
-I
,-L
und-l
manuell an, z.B:CFLAGS=-I
/home/b/root/include
-g -Wall -O3 LDLIBS=-L/home/b/root/lib
-lweirdlib
-
Nachdem du eine Bibliothek und ihren Speicherort in die Zeilen
LDLIBS
undCFLAGS
eingefügt hast und weißt, dass sie auf deinem System funktioniert, gibt es kaum noch einen Grund, sie zu entfernen. Macht es dir wirklich etwas aus, dass die endgültige ausführbare Datei 10 Kilobyte größer sein könnte, als wenn du für jedes Programm ein neues Makefile anpasst? Das bedeutet, dass du ein Makefile schreiben kannst, das zusammenfasst, wo sich alle Bibliotheken auf deinem System befinden, und es von Projekt zu Projekt kopieren kannst, ohne es neu zu schreiben. -
Wenn dein Programm eine zweite (oder mehrere) C-Datei(en) benötigt, füge
second
.o,third
.o usw. in die ZeileOBJECTS
(keine Kommas, nur Leerzeichen zwischen den Namen) im Makefile am Anfang dieses Abschnitts ein. -
Wenn du ein Programm hast, das aus einer einzigen .c-Datei besteht, brauchst du vielleicht gar kein Makefile. In einem Verzeichnis ohne Makefile und erf.c von vorhin kannst du versuchen, deine Shell zu benutzen:
export CFLAGS='-g -Wall -O3 -std=gnu11' export LDLIBS='-lm' make erf
und beobachte, wie
make
sein Wissen über die C-Kompilierung nutzt, um den Rest zu erledigen.
Bibliotheken aus der Quelle verwenden
Bis jetzt ging es darum, deinen eigenen Code mit make
zu kompilieren. Das Kompilieren von Code, der von anderen bereitgestellt wird, ist oft eine andere Geschichte.
Lass uns ein Beispielpaket ausprobieren. Die GNU Scientific Library enthält eine Vielzahl von Routinen für numerische Berechnungen.
Die GSL wird über Autotools verpackt, eine Reihe von Werkzeugen, die eine Bibliothek für die Verwendung auf jedem Rechner vorbereiten, indem sie jede bekannte Macke testen und die entsprechende Abhilfe implementieren. Wie du deine eigenen Programme und Bibliotheken mit Autotools verpacken kannst, erfährst du in "Packaging Your Code with Autotools". Aber jetzt können wir erst einmal als Nutzer des Systems anfangen und die Leichtigkeit genießen, nützliche Bibliotheken schnell zu installieren.
Die GSL wird oft in vorkompilierter Form über den Paketmanager bereitgestellt, aber um die Schritte der Kompilierung durchzugehen, erfährst du hier, wie du die GSL als Quellcode bekommst und einrichtest, vorausgesetzt, du hast Root-Rechte auf deinem Computer.
wget ftp://ftp.gnu.org/gnu/gsl/gsl-1.16.tar.gz tar xvzf gsl-*gz cd gsl-1.16 ./configure make sudo make install
-
Lade das gezippte Archiv herunter. Bitte deinen Paketmanager,
wget
zu installieren, wenn du es nicht hast, oder gib diese URL in deinen Browser ein. -
Entpacke das Archiv:
x
=extract,v
=verbose,z
=unzip viagzip
,f
=filename. -
Bestimme die Eigenheiten deines Rechners. Wenn du im Schritt
configure
eine Fehlermeldung über ein fehlendes Element erhältst, nutze deinen Paketmanager, um es zu beschaffen und führeconfigure
erneut aus. -
Installiere am richtigen Ort - wenn du die entsprechenden Rechte hast.
Wenn du das zu Hause ausprobierst, hast du wahrscheinlich Root-Rechte und es wird gut funktionieren. Wenn du auf der Arbeit bist und einen gemeinsam genutzten Server verwendest, sind die Chancen gering, dass du Superuser-Rechte hast. Du wirst also nicht in der Lage sein, das Passwort anzugeben, das du brauchst, um den letzten Schritt des Skripts als Superuser auszuführen. In diesem Fall musst du dich bis zum nächsten Abschnitt gedulden.
Wurde es installiert? In Beispiel 1-3 findest du ein kurzes Programm, mit dem du versuchen kannst, das 95 %-Konfidenzintervall mit Hilfe der GSL-Funktionen zu ermitteln; probiere es aus und schau, ob du es verknüpfen und zum Laufen bringen kannst:
Beispiel 1-3. Wiederholung von Beispiel 1-1 mit der GSL (gsl_erf.c)
#include <gsl/gsl_cdf.h>
#include <stdio.h>
int
main
(){
double
bottom_tail
=
gsl_cdf_gaussian_P
(
-
1.96
,
1
);
printf
(
"Area between [-1.96, 1.96]: %g
\n
"
,
1
-
2
*
bottom_tail
);
}
Um die soeben installierte Bibliothek zu verwenden, musst du das Makefile deines bibliotheksverwendenden Programms ändern, um die Bibliotheken und ihre Speicherorte anzugeben.
Je nachdem, ob du pkg-config
zur Hand hast, kannst du eines davon machen:
LDLIBS=`pkg-config --libs gsl` #or LDLIBS=-lgsl -lgslcblas -lm
Wenn es nicht an einem Standardort installiert wurde und pkg-config
nicht verfügbar ist, musst du Pfade zu den Köpfen dieser Definitionen hinzufügen, z.B. CFLAGS=-I/usr/local/include
und LDLIBS=-L/usr/local/lib -Wl,-R/usr/local/lib
.
Bibliotheken aus dem Quellcode verwenden (auch wenn dein Sysadmin das nicht will)
Du hast vielleicht keinen Root-Zugang, wenn du einen gemeinsam genutzten Computer auf der Arbeit benutzt oder wenn du zu Hause einen besonders kontrollierenden Partner hast. Dann musst du in den Untergrund gehen und dein eigenes privates Root-Verzeichnis erstellen.
Der erste Schritt besteht darin, das Verzeichnis einfach zu erstellen:
mkdir ~/root
Ich habe bereits ein ~/tech-Verzeichnis, in dem ich alle meine technische Logistik, Handbücher und Codeschnipsel aufbewahre, also habe ich ein ~/tech/root-Verzeichnis erstellt. Der Name spielt keine Rolle, aber ich benutze ~/root
als das Dummy-Verzeichnis hier.
Hinweis
Deine Shell ersetzt die Tilde durch den vollständigen Pfad zu deinem Heimatverzeichnis und erspart dir so eine Menge Tipparbeit. Der POSIX-Standard verlangt, dass die Shell dies nur am Anfang eines Wortes oder direkt nach einem Doppelpunkt tut (was du für eine Pfadvariable brauchst), aber die meisten Shells erweitern Tilden auch in der Mitte eines Wortes. Andere Programme, wie z. B. make
, erkennen die Tilde möglicherweise nicht als dein Heimatverzeichnis. In diesen Fällen kannst du die von POSIX vorgeschriebene Umgebungsvariable HOME verwenden, wie in den folgenden Beispielen.
Der zweite Schritt besteht darin, den richtigen Teil deines neuen Root-Systems zu allen relevanten Pfaden hinzuzufügen. Bei Programmen ist das die PATH
in deiner .bashrc (oder einer entsprechenden Datei):
PATH=~/root/bin
:$PATH
Wenn du das Unterverzeichnis bin deines neuen Verzeichnisses vor das ursprüngliche PATH
setzt, wird es zuerst durchsucht und deine Kopie aller Programme wird zuerst gefunden. So kannst du alle Programme, die sich bereits in den gemeinsamen Standardverzeichnissen des Systems befinden, durch deine bevorzugte Version ersetzen.
Für die Bibliotheken, die du in deine C-Programme einbindest, notiere die neuen Pfade, die du im vorhergehenden Makefile suchen musst:
LDLIBS=-L$(HOME)/root/lib
(plus the other flags, like -lgsl -lm ...) CFLAGS=-I$(HOME)/root/include
(plus -g -Wall -O3 ...)
Jetzt, wo du ein lokales Root-System hast, kannst du es auch für andere Systeme verwenden, z. B. für Javas CLASSPATH
.
Der letzte Schritt besteht darin, Programme in deinem neuen Stammverzeichnis zu installieren. Wenn du den Quellcode hast und er Autotools verwendet, musst du nur noch Folgendes hinzufügen --prefix=
$HOME/root
an der richtigen Stelle einfügen:
./configure --prefix=$HOME/root
&& make && make install
Du brauchst sudo
nicht, um den Installationsschritt zu machen, denn jetzt liegt alles in deinem Einflussbereich.
Da die Programme und Bibliotheken in deinem Home-Verzeichnis liegen und nicht mehr Rechte haben als du, kann sich dein Sysadmin nicht beschweren, dass sie eine Zumutung für andere sind. Wenn sich dein Systemadministrator trotzdem beschwert, dann ist es, so traurig das auch sein mag, vielleicht an der Zeit, sich zu trennen.
C-Programme über Here Document kompilieren
An diesem Punkt hast du das Muster der Zusammenstellung schon ein paar Mal gesehen:
-
Setzt eine Variable, die Compilerflags ausdrückt.
-
Setze eine Variable, die Linker-Flags ausdrückt, einschließlich eines
-l
Flags für jede Bibliothek, die du verwendest. -
Verwende
make
oder die Rezepte deiner IDE, um die Variablen in vollständige Kompilier- und Linkbefehle umzuwandeln.
Im restlichen Teil dieses Kapitels werden wir all das ein letztes Mal tun, und zwar mit einer absolut minimalen Einrichtung: nur der Shell. Wenn du ein kinetischer Lerner bist, der Skriptsprachen durch Ausschneiden und Einfügen von Codeschnipseln in den Interpreter erlernt hat, kannst du dasselbe mit dem Einfügen von C-Code in deine Eingabeaufforderung tun.
Einbinden von Header-Dateien über die Kommandozeile
gcc
und clang
haben ein praktisches Flag zum Einfügen von Kopfzeilen. Zum Beispiel:
gcc -include stdio.h
ist gleichbedeutend mit
#include <stdio.h>
am Anfang deiner C-Datei; das Gleiche gilt für clang -include stdio.h
.
Wenn wir das zu unserem Compileraufruf hinzufügen, können wir hello.c endlich als die eine Codezeile schreiben, die sie sein sollte:
int
main
(){
printf
(
"Hello, world.
\n
"
);
}
die sich gut übersetzen lässt:
gcc -include stdio.h hello.c -o hi --std=gnu99 -Wall -g -O3
oder Shell-Befehle wie:
export CFLAGS='-g -Wall -include stdio.h' export CC=c99 make hello
Dieser Tipp über -include
ist compilerspezifisch und beinhaltet das Verschieben von Informationen aus dem Code in die Kompilieranweisungen. Wenn du denkst, dass das eine schlechte Form ist, dann überspringe diesen Tipp.
Die einheitliche Überschrift
Erlaube mir, für ein paar Absätze auf das Thema Header-Dateien abzuschweifen. Um nützlich zu sein, muss eine Header-Datei die Typendefinitionen, Makrodefinitionen und Funktionsdeklarationen für Typen, Makros und Funktionen enthalten, die von der Codedatei, die den Header enthält, verwendet werden. Außerdem sollte sie keine Typendefinitionen, Makrodefinitionen und Funktionsdeklarationen enthalten, die von der Codedatei nicht verwendet werden.
Um diese beiden Bedingungen wirklich zu erfüllen, müsstest du für jede Codedatei einen eigenen Header schreiben, der genau die relevanten Teile für die aktuelle Codedatei enthält. Das macht eigentlich niemand.
Es gab einmal eine Zeit, in der Compiler mehrere Sekunden oder Minuten brauchten, um selbst relativ einfache Programme zu kompilieren. Meine aktuellen Kopien von stdio.h und stdlib.h sind jeweils etwa 1.000 Zeilen lang (probier mal wc -l /usr/include/stdlib.h
) und time.h weitere 400, was bedeutet, dass dieses siebenzeilige Programm:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int
main
(){
srand
(
time
(
NULL
));
// Initialize RNG seed.
printf
(
"%i
\n
"
,
rand
());
// Make one draw.
}
ist eigentlich ein ~2.400 Zeilen langes Programm.
Dein Compiler hält 2.400 Zeilen nicht mehr für eine große Sache und kompiliert den Text in weniger als einer Sekunde. Der Trend geht also dahin, den Benutzern Zeit bei der Auswahl von Kopfzeilen zu sparen, indem mehr Elemente in eine einzige Kopfzeile aufgenommen werden.
Du wirst später Beispiele sehen, die GLib verwenden, mit einer #include <glib.h>
am Anfang. Diese Kopfzeile enthält 74 Unterüberschriften, die alle Unterabschnitte der GLib-Bibliothek abdecken. Das GLib-Team hat die Benutzeroberfläche gut gestaltet, denn wer keine Zeit damit verbringen will, die richtigen Unterabschnitte der Bibliothek herauszusuchen, kann sich in einer Zeile durch den Header-Papierkram wühlen, während diejenigen, die eine detaillierte Kontrolle wünschen, genau die Header auswählen können, die sie brauchen. Es wäre schön, wenn die C-Standardbibliothek einen schnellen und einfachen Header wie diesen hätte; in den 1980er Jahren war das noch nicht üblich, aber es ist leicht, einen zu erstellen.
Wenn du einen öffentlichen Header für andere Benutzer schreibst, sollte dein Header gemäß der Regel, dass ein Header keine unnötigen Elemente enthalten sollte, wahrscheinlich keine #include "allheads.h"
haben, die alle Definitionen und Deklarationen der Standardbibliothek enthält. Das ist in der Regel richtig: Deine Bibliothek hat vielleicht ein Codesegment, das die verknüpften Listen von GLib benutzt, um zu arbeiten, aber das bedeutet, dass du #include <glib.h>
in dieser Codedatei und nicht im Header der öffentlichen Bibliothek aufrufen musst.
Um auf die Idee zurückzukommen, eine schnelle Kompilierung auf der Kommandozeile einzurichten, macht der einheitliche Header das Schreiben schneller Programme noch schneller. Sobald du einen einheitlichen Header hast, ist sogar eine Zeile wie #include <allheads.h>
überflüssig, wenn du gcc
oder clang
benutzt, weil du stattdessen -include allheads.h
zu deinem CFLAGS
hinzufügen kannst und nie wieder darüber nachdenken musst, welche projektfremden Header du einfügen musst.
Hier Dokumente
Hier sind Dokumente eine Funktion von POSIX-Standard-Shells, die du für C, Python, Perl oder was auch immer verwenden kannst, und sie machen dieses Buch viel nützlicher und lustiger. Wenn du ein mehrsprachiges Skript erstellen willst, sind diese Dokumente ein einfacher Weg, dies zu tun. Du kannst in Perl parsen, in C rechnen und dann mit Gnuplot die hübschen Bilder erstellen.
Hier ist ein Python-Beispiel. Normalerweise würdest du Python sagen, dass es ein Skript ausführen soll:
python your_script.py
In Python kannst du den Dateinamen -
angeben, um stdin als Eingabedatei zu verwenden:
echo "print 'hi.'" | python -
Theoretisch könntest du einige lange Skripte über echo
in die Befehlszeile eingeben, aber du wirst schnell feststellen, dass es viele kleine, unerwünschte Parsings gibt - du brauchst zum Beispiel \"hi\"
statt "hi"
.
Daher das Dokument hier, das überhaupt kein Parsing durchführt. Versuch das mal:
python - <<"XXXX" lines=2 print "\nThis script is %i lines long.\n" %(lines,) XXXX
-
Diese Dokumente sind eine Standard-Shell-Funktion, d.h. sie sollten auf jedem POSIX-System funktionieren.
-
"XXXX"
ist eine beliebige Zeichenkette;"EOF"
ist ebenfalls beliebt, und"-----"
sieht gut aus, solange die Anzahl der Bindestriche oben und unten übereinstimmt. Wenn die Shell die von dir gewählte Zeichenkette allein in einer Zeile sieht, hört sie auf, das Skript an die stdin des Programms zu senden. Das ist alles, was beim Parsen passiert. -
Es gibt auch eine Variante, die mit
<<-
beginnt. Bei dieser Variante werden alle Tabulatoren am Anfang jeder Zeile entfernt, so dass du ein here-Dokument in einen eingerückten Abschnitt eines Shell-Skripts einfügen kannst, ohne den Fluss der Einrückung zu unterbrechen. Für ein here-Dokument in Python wäre das natürlich eine Katastrophe. -
Als weitere Variante gibt es einen Unterschied zwischen
<<"XXXX"
und<<XXXX
. In der zweiten Version parst die Shell bestimmte Elemente, d.h. du kannst die Shell den Wert von$
shell_variables
für dich einfügen. Die Shell verlässt sich bei ihren Variablen und anderen Erweiterungen stark auf$
;$
ist eines der wenigen Zeichen auf einer Standardtastatur, das für C keine besondere Bedeutung hat. Es ist, als ob die Leute, die Unix geschrieben haben, es von Grund auf so konzipiert haben, dass es einfach ist, Shell-Skripte zu schreiben, die C-Code erzeugen....
Kompilieren von stdin
OK, zurück zu C: Wir können hier Dokumente verwenden, um C-Code zu kompilieren, der über gcc
oder clang
in die Kommandozeile eingefügt wurde.
Wir werden das Makefile nicht verwenden, also brauchen wir einen einzigen Kompilierbefehl. Um uns das Leben weniger schwer zu machen, geben wir ihm einen Alias. Füge diesen Befehl in deine Befehlszeile ein oder füge ihn in deine .bashrc, .zshrc oder wo auch immer ein:
go_libs="-lm"
go_flags="-g -Wall -include allheads.h
-O3"
alias go_c="c99 -xc - $go_libs $go_flags"
wobei allheads.h
der Aggregatkopf ist, den du zuvor zusammengestellt hast. Die Verwendung des -include
Flag bedeutet, dass du beim Schreiben des C-Codes an eine Sache weniger denken musst, und ich habe festgestellt, dass die Geschichte der Bash durcheinander gerät, wenn #
im C-Code vorkommt.
In der Kompilierzeile erkennst du an -
, dass du stdin verwenden sollst, anstatt aus einer benannten Datei zu lesen. -xc
weist dies als C-Code aus, denn gcc steht für GNU Compiler Collection und nicht für GNU C Compiler. Da der Name der Eingabedatei nicht auf .c endet, müssen wir uns darüber im Klaren sein, dass es sich hier nicht um Java, Fortran, Objective C, Ada oder C++ handelt (und das Gleiche gilt für clang
, auch wenn der Name die Sprache C aufrufen soll).
Was auch immer du für die Anpassung von LDLIBS
und CFLAGS
in deinem Makefile getan hast, tue es hier.
Jetzt sind wir startklar und können C-Code auf der Kommandozeile kompilieren:
go_c << '---' int main(){printf("Hello from the command line.\n");} --- ./a.out
Wir können hier ein Dokument verwenden, um kurze C-Programme auf der Kommandozeile einzufügen und kleine Testprogramme zu schreiben, ohne dass es zu Problemen kommt. Du brauchst nicht nur kein Makefile, sondern nicht einmal eine Eingabedatei.4
Erwarte nicht, dass diese Art von Arbeit deine Hauptaufgabe ist. Aber das Ausschneiden und Einfügen von Codeschnipseln in die Kommandozeile kann Spaß machen, und die Möglichkeit, einen einzelnen Schritt in C in einem längeren Shell-Skript zu haben, ist ziemlich fabelhaft.
1 Cygwin ist ein von Red Hat, Inc. betriebenes Projekt, das es den Nutzern auch ermöglicht, das Recht zu erwerben, ihren Quellcode nicht gemäß der GPL weiterzugeben.
2 Obwohl MinGW über einen Paketmanager verfügt, der die Systemgrundlagen installiert und eine Reihe von Bibliotheken bereitstellt (hauptsächlich die, die für MinGW selbst benötigt werden), verblasst diese Handvoll vorkompilierter Bibliotheken im Vergleich zu den Hunderten von Paketen, die der typische Paketmanager bereitstellt. Tatsächlich hat der Paketmanager für meine Linux-Box mehr MinGW-kompilierte Bibliotheken als der MinGW-Paketmanager. Dies ist der aktuelle Stand; wenn du dies liest, haben Nutzer wie du vielleicht schon mehr Pakete zum MinGW-Repository beigetragen.
3 Du kannst find / -type f | wc -l
ausprobieren, um eine ungefähre Dateizählung auf jedem System mit POSIX-Standard zu erhalten.
4 Es gibt einen POSIX-Brauch, dass, wenn die erste Zeile von file
ist #!
aninterpreter
ist, dann wird beim Ausführen von file
von der Shell aus ausführt, wird die Shell tatsächlich aninterpreter
file
. Das funktioniert gut bei interpretierten Sprachen wie Perl oder Python (vor allem, weil diese Sprachen #
als Kommentarmarker verwenden und daher die erste Zeile ignorieren). Mit den Hinweisen in diesem Abschnitt könntest du ein Skript schreiben (nennen wir es c99sh
), das mit einer C-Datei, die mit #!c99sh
beginnt, das Richtige tut: die erste Zeile korrigieren, den Rest der Datei über eine Pipe an den Compiler schicken und dann das resultierende Programm ausführen. Rhys Ulerich hat jedoch bereits ein solches c99sh
für dich geschrieben und das Skript auf GitHub veröffentlicht.
Get 21st Century C, 2. Auflage now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.