GNU/Linux >> LINUX-Kenntnisse >  >> Linux

.o-Dateien vs. .a-Dateien

.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“


Linux
  1. Dateien ohne Trennzeichen einfügen?

  2. Grep:Speicher erschöpft?

  3. Dateien im Verzeichnis umbenennen?

  4. Dd:Mehrere Eingabedateien?

  5. Linux-ls-Befehl - Dateien auflisten

Finden Sie große Dateien in Linux

Rm-Befehl unter Linux

Versteckte Dateien anzeigen

Dateien in Ubuntu 22.04 finden

ls-Befehl unter Linux/UNIX

So finden Sie Dateien in Debian