Многократное определение функции в коде C / C ++ - PullRequest
7 голосов
/ 27 марта 2012

Это вопрос об определениях функций C / C ++.Обсуждаемый код представляет собой статическую библиотеку libRmath, которая предоставляет определения в заголовочном файле Rmath.h в R.

Документация , предоставленная для библиотеки, гласит, что для пользователя необязательно указывать определение функции для функции double unif_rand(void).

Поэтому мой вопрос заключается в том, является ли такое определение функции необязательным,не было бы проблемы с множественными определениями функций, которые не разрешены в C/C++?

Edit: Может быть заманчиво рассуждать о том, как все работает, не глядя на исходный код, но это не то, что я хочу.Мне интересно знать, как это действительно работает, поэтому вам, вероятно, потребуется прочитать исходный код и документацию , чтобы ответить на этот вопрос.

Ответы [ 2 ]

15 голосов
/ 27 марта 2012

Когда ваше приложение связано, неразрешенные символы будут разрешаться с использованием предоставленных вами библиотек.Если вы не определили функцию, она будет неразрешенным символом во время компоновки, поэтому связанный в этом случае попытается разрешить этот символ с помощью librmath.Если один или несколько символов не могут быть разрешены, вы получите ошибку компоновщика.

Однако, если вы определите функцию в своем коде, она будет уже определена во время компоновки, поэтому не будетнеобходимо разрешить его, используя символы из внешних библиотек.

То, что вы не можете сделать, это определить один и тот же символ более одного раза в вашем приложении.

Редактировать : св другом ответе много споров, я привел практический пример.Я создал общий объект (аналог DLL в Windows), который определяет и экспортирует функцию 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();
}

Чтобы протестировать этот общий объект, я создал приложение, котороесвязан с ним:

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

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

int main() {
    bar();
}

Я скомпилировал совместно используемый объект и приложение:

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

И когда я выполняю test, задаю путь к библиотеке ".", так что мой общий объект может быть загружен, я получаю следующий вывод:

matias@master:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app

Как видите, вызывается функция foo, определенная в приложении (не общий объект).Вы можете сделать это в основном для каждого экспортируемого символа в общем объекте.

EDIT2 : я добавил еще одну экспортированную функцию в lib.h.Приложение теперь вызывает эту функцию, которая в итоге вызывает foo.Результат такой же, как и ожидалось.

EDIT3 : Хорошо, давайте углубимся.Это дамп из функции bar:

Dump of assembler code for function bar@plt:
   0x0804855c <+0>: jmp    DWORD PTR ds:0x804a004
   0x08048562 <+6>: push   0x8
   0x08048567 <+11>:    jmp    0x804853c

Если мы перейдем по адресу 0x804a004:

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

Как вы видите, он переходит в таблицу глобальных смещений.Вы можете прочитать о GOT здесь и здесь .Динамические символы (которые разрешаются во время выполнения) хранятся в этой таблице.Всякий раз, когда вы вызываете символ, который должен быть разрешен во время выполнения, вы фактически переходите к этой таблице, а затем переходите к адресу, который хранится в соответствующей записи этой таблицы.Поскольку приложение определяет foo, GOT содержит адрес определения из test.cpp, а не адрес в нашем общем объекте.

EDIT4 : Хорошо, последнее редактирование.Цитирование из документации:

Вам необходимо будет снабдить универсальный генератор случайных чисел

 double unif_rand(void)

или использовать тот, который прилагается (и с динамической библиотекой или DLL, вам придетсяиспользуйте предоставленный (...)

В документации ясно сказано, что вы не можете предоставить собственную реализацию unif_rand, если используете динамическую библиотеку. Поэтому я считаю, что то, на что я указална самом деле отвечает на ваш вопрос.

0 голосов
/ 27 марта 2012

Связывание статической библиотеки немного отличается от связывания всех объектов внутри статической библиотеки.

Определения в статической библиотеке извлекаются только при необходимости, поэтому они не могут вызывать множественные ошибки определений.

У этого есть некоторые побочные эффекты, например общеизвестная проблема глобальных инициализаторов внутри статической библиотеки, не работающей, когда ничего не указано в основной программе этот объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...