2016-04-21 80 views
0

我到處尋找鏈接器的實際功能(除了重新定位,有些似乎放在「加載」下),人們給出了兩個(比如說)C模塊的例子,它們可以互相調用函數。預處理與鏈接

據我所知,這應該通過預處理(#include等)來處理。我可以理解,如果我想在不同的時間組裝兩個模塊,我需要稍後鏈接它們。但是,當我使用gcc時,它直接輸出可執行文件,所以在這種情況下,除非預處理(我猜會是遞歸的)最終命中目標代碼時,我並不真正看到鏈接點。

注:我指的是靜態鏈接。當涉及到動態鏈接時,我可以看到這一點。

+1

首先將代碼編譯爲對象文件,然後您可以稍後將它們鏈接起來,這樣您無需在構建時(可能需要一段時間或大型項目)重新編譯所有內容。 – Majora320

+1

#include用於包含聲明,而不是實際的代碼。頭文件通常不包含可執行代碼(至少在C中)。 – rici

+0

'gcc'是一個工具驅動程序,它調用C編譯器('cc1')編譯 並調用鏈接器('ld')進行鏈接。這就是爲什麼你可能會想象 它「直接輸出可執行文件」而無需鏈接。它沒有。 您可以通過傳遞'-c' 選項來指示'gcc' * not *來調用鏈接器。嘗試以這種方式構建程序。 –

回答

1

gcc正在使用很多工具來創建可執行文件,編譯器就是其中之一,鏈接器,預處理器等也是如此,通過它可以查看它實際上在做什麼,並通過-v選項。如果你想看到的中間文件使用-save-temps即一旦你創建了以下嘗試這個文件......

gcc-5 -save-temps -v -std=c11 -Wall -pedantic-errors main.c foo.c 

如果在您的目錄,你會看到幾個額外的文件看...

a.out // Executable when no name is specified 
foo.c // original foo.c 
foo.h // original foo.h 
foo.i // Preprocessed ouput of foo.c and foo.h 
foo.o // foo Object file 
foo.s // foo assembler 
main.c // original main.c 
main.i // main preprocessed 
main.o // main object file 
main.s // main assembler 

如果您在main.i看,你會發現它包含此

# 1 "foo.h" 1 


int foo(int i); 

,但它不知道什麼函數foo實際執行。全部在foo.i。這也意味着在創建目標文件時main.o知道它需要foo函數。將main.o中的foo連接到foo.o中的foo是鏈接器正在做的事情。

下面是一個例子,創建以下三個文件

foo.h中

#ifndef FOO_H 
#define FOO_H 
int foo(int i); 
#endif 

foo.c的

#include "foo.h" 
int foo(int i) { 
    return i - 1729; 
} 

的main.c

#include "foo.h" 
#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
int main(void) { 
    printf("%d\n", foo(1)); 
    return 0; 
} 

創建自己的目標文件即不是可執行如下,請注意我已經添加了冗長的標誌,以這個..

gcc-5 -v -std=c11 -Wall -pedantic-errors main.c foo.c -c 

如果您已經使用了-o標誌,你會看到實際的鏈接命令GCC稱爲將對象鏈接成可執行文件,即

gcc-5 -v -std=c11 -Wall -pedantic-errors main.c foo.c -o fooer 

此時,您會在目錄中找到兩個目標文件。您可以使用ld創建可執行文件,如下所示(請注意,這是特定於mac的,使用gcc -v的輸出爲您的機器找到正確的命令)。

ld -dynamic -arch x86_64 -macosx_version_min 10.11.3 -lSystem main.o foo.o -o fooer