2012-03-27 9 views
7

Dies ist eine Frage zu C/C++ - Funktionsdefinitionen. Der beschriebene Code ist die statische Bibliothek libRmath, die die Definitionen in der Rmath.h Header-Datei in R bereitstellt.Mehrfache Definition einer Funktion in C/C++ - Code

Die für die Bibliothek bereitgestellte documentation besagt, dass es für den Benutzer optional ist, die Funktionsdefinition für die Funktion double unif_rand(void) bereitzustellen.

Also meine Frage ist, ob solch eine Funktionsdefinition optional ist, würde es nicht ein Problem von mehreren Funktionsdefinitionen geben, die in C/C++ nicht erlaubt ist?

Edit: Es könnte verlockend sein, darüber zu spekulieren, wie die Dinge funktionieren ohne den Quellcode zu suchen, aber das ist nicht das, was ich will. Ich bin interessiert zu wissen, wie es wirklich funktioniert, so müssten Sie wahrscheinlich die source code und documentations lesen, um diese Frage zu beantworten.

+0

Meine Vermutung wäre, dass die Funktion def in einer #ifdef ist, so dass, wenn Sie es definieren, Ihre zuerst durch den Linker zuerst gefunden wird, so dass die bereits dort würde ignoriert werden. – baash05

Antwort

15

Wenn Sie eine Anwendung verknüpfen, werden nicht aufgelöste Symbole mithilfe der von Ihnen bereitgestellten Bibliotheken aufgelöst. Wenn Sie eine Funktion nicht definieren, wird sie während des Linkens ein ungelöstes Symbol sein. Daher versucht der Link in diesem Fall, das Symbol mithilfe von librmath aufzulösen. Wenn ein oder mehrere Symbole nicht aufgelöst werden können, erhalten Sie einen Linker-Fehler.

Wenn Sie jedoch die Funktion in Ihrem Code definieren, wird sie bereits während des Linkens definiert, so dass Sie sie nicht mit Symbolen aus externen Bibliotheken auflösen müssen.

Was Sie nicht tun können, ist das gleiche Symbol mehr als einmal in Ihrer Anwendung zu definieren.

Bearbeiten: Da gibt es eine Menge Debatte in einer anderen Antwort, habe ich ein praktisches Beispiel gemacht. Ich habe ein gemeinsam genutztes Objekt (ähnlich einer DLL in Windows) erstellt, die definiert und exportiert eine Funktion foo:

//lib.h 
extern "C" { 
    void foo(); 
    void bar(); 
}; 

//lib.cpp 
#include <iostream> 
#include "lib.h" 

void foo() { 
    std::cout << "From lib\n"; 
} 

void bar() { 
    std::cout << "Bar, calling foo\n"; 
    foo(); 
} 

Um dieses gemeinsame Objekt zu testen, habe ich eine Anwendung erstellt, die damit verbunden ist, :

//test.cpp 
#include <iostream> 
#include "lib.h" 

void foo() { 
    std::cout << "From app\n"; 
} 

int main() { 
    bar(); 
} 

ich habe beide zusammengestellt, das gemeinsame Ziel und die Anwendung:

g++ lib.cpp -o libtest.so -Wall -fPIC -shared -Wl,--export-dynamic -Wl,-soname,libtest.so -Wl,-z,defs 
g++ test.cpp -o test -L. -ltest 

und wenn ich test ausführen, den Bibliothekspfad zu 01.239.140 Einstellung, so mein gemeinsames Objekt geladen werden kann, erhalte ich diese Ausgabe:

[email protected]:/tmp$ LD_LIBRARY_PATH="." ./test 
Bar, calling foo 
From app 

Wie Sie sehen können, die foo Funktion in der Anwendung definiert (nicht das Shared Object) aufgerufen wird. Sie können dies grundsätzlich für jedes exportierte Symbol in einem gemeinsamen Objekt tun.

EDIT2: Ich habe eine andere exportierte Funktion in lib.h. Die Anwendung ruft nun diese Funktion auf, die foo aufruft. Das Ergebnis ist wie erwartet dasselbe.

EDIT3: Ok, lass uns tiefer gehen.Dies ist die Müllhalde von Funktion bar:

Dump of assembler code for function [email protected]: 
    0x0804855c <+0>: jmp DWORD PTR ds:0x804a004 
    0x08048562 <+6>: push 0x8 
    0x08048567 <+11>: jmp 0x804853c 

Wenn wir gehen 0x804a004 Adresse:

Dump of assembler code for function _GLOBAL_OFFSET_TABLE_: 
    0x08049ff4 <+0>: or  BYTE PTR [edi+0x804],bl 
    0x08049ffa <+6>: add BYTE PTR [eax],al 
    0x08049ffc <+8>: add BYTE PTR [eax],al 
    0x08049ffe <+10>: add BYTE PTR [eax],al 
    ..... 

Wie Sie sehen können, ist es an den Global Offset Tabelle springen. Sie können über die GOT here und here lesen. Dynamische Symbole (die zur Laufzeit aufgelöst werden) werden in dieser Tabelle gespeichert. Wenn Sie ein Symbol aufrufen, das zur Laufzeit aufgelöst werden soll, springen Sie tatsächlich zu dieser Tabelle und springen dann zu der Adresse, die im entsprechenden Eintrag der Tabelle gespeichert ist. Da die Anwendung foo definiert, enthält der GOT die Adresse der Definition von test.cpp, nicht die in unserem gemeinsamen Objekt.

EDIT4: Okay, letzte Änderung. Zitiert aus der Dokumentation:

Sie müssen dem einheitlichen Zufallszahlengenerator

double unif_rand(void) 

oder verwenden Sie die mitgelieferten (und mit einer dynamischen Bibliothek oder DLL angeben, den Sie haben die man verwenden geliefert (...)

die Dokumentation sagt klar, dass Sie nicht, dass Sie Implementierung von unif_rand besitzen zur Verfügung stellen kann, wenn Sie die dynamische Bibliothek verwenden. Deshalb glaube ich, dass das, was ich darauf hinwies, tatsächlich ans Wers deine Frage.

+0

Das sagt im Wesentlichen dasselbe wie ich gesagt habe. – Nawaz

+0

Der letzte Absatz ist im Wesentlichen der gleiche. Der Rest ist nicht. – mfontanini

+0

Wie genau? Der Rest ist nur die Details. – Nawaz

0

Die Verknüpfung einer statischen Bibliothek unterscheidet sich geringfügig von der Verknüpfung aller Objekte in der statischen Bibliothek.

Definitionen in der statischen Bibliothek werden nur bei Bedarf abgerufen, sodass sie nicht zu mehreren Definitionsfehlern führen können.

Dies hat einige Nebenwirkungen, zum Beispiel thewell-knownproblem globale Initialisierer in einer statischen Bibliothek nicht ausgeführt, wenn nichts im Hauptprogramm dieses Objekt verweist.

+0

Ein kleiner Kommentar; es könnte sich lohnen zu erklären "zum Beispiel das bekannte Problem der globalen Initialisierer in einer statischen Bibliothek, die nicht läuft, wenn nichts im Hauptprogramm auf dieses Objekt verweist", weil das nicht wie ein Problem klingt, sondern korrekt klingt; "Objekt nicht verwendet, also kein Overhead der Initialisierung" klingt besser als "obwohl das Objekt nicht verwendet wird, gibt es immer noch den Overhead der Initialisierung". Gibt es bereits eine Stackoverflow-Frage, die als Referenz verwendet werden könnte? Nur ein Gedanke. – gbulmer

+0

Können Sie mit einigen C++ - Code und Kompilierungsbefehlen arbeiten? – ggg

+0

@gbulmer: http://stackoverflow.com/questions/6317796/ctor-init-not-calling-the-global-ctor-instances-in-library und http://stackoverflow.com/questions/9459980/c- global-variable-nicht-initialisiert-wenn-verbunden-durch-statische-Bibliotheken-aber-ok und http://stackoverflow.com/questions/6221947/ensuring-that-a-static-method-gets-called-before- Main –

Verwandte Themen