2010-07-15 23 views
1

當我們建立一個長期計劃,一些符號鏈接時(像那些在.lib)加以解決,關於C編譯器的內部邏輯

,但一些可以在運行時(那些在.dll解決),

我的疑問是編譯器如何知道這個,或者我們如何通知編譯器呢?

回答

1

您必須聲明原型的功能,其主體不可用編譯時間

您可以通過包括適當的頭(.h文件)將包含像這樣的定義,這樣做:

int foo(int bar);

注缺乏身體裏的。

通常與共享庫還有一個間接層,其中包含函數指針的結構形成。加載庫時,它會調整函數指針以引用共享庫中包含的函數。

+0

我知道了'.h',卻怎麼也編譯知道'foo'應在鏈接時得到解決或運行只是基於該聲明的時間? – wamp 2010-07-15 02:04:58

+0

@wamp:當你鏈接到一個動態庫,而不是自己編寫'dlopen'調用時,你會向鏈接器傳遞一個參數,告訴它哪些庫要搜索符號。例如,'-lpthread'會導致gcc鏈接到libpthread.so並允許使用'pthread_create'。 – Borealid 2010-07-15 02:49:44

0

那些可以在鏈接時解決的是;那些不能在運行時在共享庫中搜索的內容。

+0

但是如果不解決,有時候我們會遇到致命的錯誤,比如我們沒有機會在運行時提供它 – wamp 2010-07-15 02:35:46

+0

-1:不是真的。如果你調用'notafunctionatall()',它不會在共享庫中自動搜索。 – Borealid 2010-07-15 03:07:10

3

當您鏈接您的代碼時,編譯器會在靜態庫和動態庫中搜索未定義的符號。如果它找到動態庫導出的動態符號,則將符號解析延遲到運行時;如果它找到一個靜態符號,它立即解析符號;如果它根本找不到符號,它會報告錯誤(除非您正在編譯共享庫,在這種情況下它可以)。

您可以使用nm -D檢查由共享庫導出的動態符號。

0

鏈接器完成這項工作。

  • 對於靜態函數,鏈接器將庫包含到您的可改變的字段中。通話是固定在記憶中的位置。
  • 對於動態庫,鏈接器爲該庫放置一個運行時「搜索器」。動態庫發佈函數列表及其相關內存地址。所以,運行時可以填充它們的函數指針列表。

動態函數的原始代碼可以編譯爲對函數指針的調用。 [的確,這是鏈接器的工作:將函數調用替換爲生成可執行文件的引用]。

+0

編譯器如果是**靜態**或**動態**函數如何編譯? – wamp 2010-07-15 03:18:10

+0

@wamp:動態符號在ELF標題中有不同的類型。 – Borealid 2010-07-15 03:45:59

+0

@Borealid,你能詳細解釋一下嗎? – wamp 2010-07-15 04:18:00

0

編譯器需要在編譯時知道函數聲明。鏈接器會在鏈接時鏈接到聲明以使其成爲可執行文件。

對於動態加載的庫,您可以插入代碼以在運行時使用dlopen dlsym和dlclose獲取符號。現在這些函數調用搜索這些符號,如果它們在動態庫中找不到,它們會返回錯誤。因此你也需要處理這個錯誤。動態庫加載不確保符號已被解析和鏈接。加載動態庫時,它仍然存在。

編輯:固定可怕的語法

+0

因此,我們**在'c代碼'中通知**編譯器,而不是'編譯器選項'? – wamp 2010-07-15 04:20:24

+0

@wamp - 我們使用編譯器選項鍊接庫。然而,在源代碼中,包含頭文件是爲了確保編譯器知道該符號。 – 2010-07-15 04:45:04