.o
Dateien sind Objekte. Sie sind die Ausgabe des Compilers und die Eingabe für den Linker/Bibliothekar.
.a
Dateien sind Archive. Sie sind Gruppen von Objekten oder statische Bibliotheken und werden ebenfalls in den Linker eingegeben.
Zusätzlicher Inhalt
Ich habe den Teil "Beispiele" Ihrer Frage nicht bemerkt. Im Allgemeinen werden Sie ein Makefile verwenden, um statische Bibliotheken zu generieren.
AR = ar
CC = gcc
objects := hello.o world.o
libby.a: $(objects)
$(AR) rcu [email protected] $(objects)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o [email protected]
Dadurch wird hello.c
kompiliert und world.c
in Objekte und archivieren sie dann in der Bibliothek. Je nach Plattform müssen Sie möglicherweise auch ein Dienstprogramm namens ranlib
ausführen um das Inhaltsverzeichnis des Archivs zu generieren.
Eine interessante Randnotiz:.a
Dateien sind technisch gesehen Archivdateien und keine Bibliotheken. Sie sind analog zu ZIP-Dateien ohne Komprimierung, obwohl sie ein viel älteres Dateiformat verwenden. Das Inhaltsverzeichnis, das von Dienstprogrammen wie ranlib
generiert wird macht ein Archiv zu einer Bibliothek . Java-Archivdateien (.jar
) ähneln sich darin, dass es sich um ZIP-Dateien handelt, die einige spezielle Verzeichnisstrukturen haben, die vom Java-Archivierer erstellt wurden.
Es gibt noch einen weiteren Aspekt beim Verlinken gegen .a
vs. .o
Dateien:beim Verlinken alle .o
s, die als Argumente übergeben werden, sind in der endgültigen ausführbaren Datei enthalten, wohingegen Einträge aus beliebigen .a
Argumente werden nur dann in die Ausgabe des Linkers aufgenommen, wenn sie eine Symbolabhängigkeit im Programm auflösen.
Genauer gesagt, jeder .a
Datei ist ein Archiv, das aus mehreren .o
besteht Dateien. Sie können an jeden .o
denken eine atomare Codeeinheit sein. Wenn der Linker ein Symbol von einer dieser Einheiten benötigt, wird die gesamte Einheit in die endgültige Binärdatei gesaugt; aber keiner der anderen, es sei denn, sie werden ebenfalls benötigt.
Im Gegensatz dazu, wenn Sie einen .o
passieren Auf der Befehlszeile saugt der Linker es ein, weil Sie es angefordert haben.
Betrachten Sie zur Veranschaulichung das folgende Beispiel, in dem wir eine statische Bibliothek mit zwei Objekten a.o
haben und b.o
. Unser Programm referenziert nur Symbole ab a.o
. Wir werden vergleichen, wie der Linker die Übergabe von a.o
behandelt und b.o
zusammen, im Vergleich zur statischen Bibliothek, die dieselben zwei Objekte umfasst.
// header.hh
#pragma once
void say_hello_a();
void say_hello_b();
// a.cc
#include "header.hh"
#include <iostream>
char hello_a[] = "hello from a";
void say_hello_a()
{
std::cout << hello_a << '\n';
}
// b.cc
#include "header.hh"
#include <iostream>
char hello_b[] = "hello from b";
void say_hello_b()
{
std::cout << hello_b << '\n';
}
// main.cc
#include "header.hh"
int main()
{
say_hello_a();
}
Wir können den Code mit diesem Makefile kompilieren:
.PHONY = compile archive link all clean
all: link
compile:
@echo ">>> Compiling..."
g++ -c a.cc b.cc main.cc
archive: compile
@echo ">>> Archiving..."
ar crs lib.a a.o b.o
link: archive
@echo ">>> Linking..."
g++ -o main_o main.o a.o b.o
g++ -o main_a main.o lib.a
clean:
rm *.o *.a main_a main_o
und erhalten Sie zwei ausführbare Dateien main_o
und main_a
die sich darin unterscheiden, dass der Inhalt von a.cc
und b.cc
wo durch zwei .o
bereitgestellt s im ersten Fall und durch einen .a
im zweiten.
Zuletzt untersuchen wir die Symbole der endgültigen ausführbaren Dateien mit dem nm
Werkzeug:
$ nm --demangle main_o | grep hello
00000000000011e9 t _GLOBAL__sub_I_hello_a
000000000000126e t _GLOBAL__sub_I_hello_b
0000000000004048 D hello_a
0000000000004058 D hello_b
0000000000001179 T say_hello_a()
00000000000011fe T say_hello_b()
$ nm --demangle main_a | grep hello
00000000000011e9 t _GLOBAL__sub_I_hello_a
0000000000004048 D hello_a
0000000000001179 T say_hello_a()
und beachten Sie diesen main_a
tatsächlich fehlen die unnötigen Symbole von b.o
. Das heißt, der Linker hat den Inhalt von b.o
nicht aufgenommen innerhalb des Archivs lib.a
weil keines der Symbole von b.cc
wurden verwiesen.
Eine .o-Datei ist das Ergebnis der Kompilierung einer einzelnen Kompilierungseinheit (im Wesentlichen eine Quellcodedatei mit zugehörigen Header-Dateien), während eine .a-Datei aus einer oder mehreren .o-Dateien besteht, die als Bibliothek gepackt sind.
D Shawleys Antwort ist gut, ich wollte nur ein paar Punkte hinzufügen, weil andere Antworten ein unvollständiges Verständnis dessen widerspiegeln, was vor sich geht.
Beachten Sie, dass Archivdateien (.a) nicht darauf beschränkt sind, Objektdateien (.o) zu enthalten. Sie können beliebige Dateien enthalten. Nicht oft nützlich, aber sehen Sie sich die in einem Archiv eingebetteten dynamischen Linker-Abhängigkeitsinformationen für einen dummen Linker-Trick an.
Beachten Sie auch, dass Objektdateien (.o) nicht unbedingt das Ergebnis einer einzelnen Kompilierungseinheit sind. Es ist möglich, mehrere kleinere Objektdateien teilweise zu einer einzigen größeren Datei zu verknüpfen.
http://www.mihaiu.name/2002/library_development_linux/ – suchen Sie auf dieser Seite nach „teilweise“