我到處尋找鏈接器的實際功能(除了重新定位,有些似乎放在「加載」下),人們給出了兩個(比如說)C模塊的例子,它們可以互相調用函數。預處理與鏈接
據我所知,這應該通過預處理(#include等)來處理。我可以理解,如果我想在不同的時間組裝兩個模塊,我需要稍後鏈接它們。但是,當我使用gcc時,它直接輸出可執行文件,所以在這種情況下,除非預處理(我猜會是遞歸的)最終命中目標代碼時,我並不真正看到鏈接點。
注:我指的是靜態鏈接。當涉及到動態鏈接時,我可以看到這一點。
我到處尋找鏈接器的實際功能(除了重新定位,有些似乎放在「加載」下),人們給出了兩個(比如說)C模塊的例子,它們可以互相調用函數。預處理與鏈接
據我所知,這應該通過預處理(#include等)來處理。我可以理解,如果我想在不同的時間組裝兩個模塊,我需要稍後鏈接它們。但是,當我使用gcc時,它直接輸出可執行文件,所以在這種情況下,除非預處理(我猜會是遞歸的)最終命中目標代碼時,我並不真正看到鏈接點。
注:我指的是靜態鏈接。當涉及到動態鏈接時,我可以看到這一點。
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
首先將代碼編譯爲對象文件,然後您可以稍後將它們鏈接起來,這樣您無需在構建時(可能需要一段時間或大型項目)重新編譯所有內容。 – Majora320
#include用於包含聲明,而不是實際的代碼。頭文件通常不包含可執行代碼(至少在C中)。 – rici
'gcc'是一個工具驅動程序,它調用C編譯器('cc1')編譯 並調用鏈接器('ld')進行鏈接。這就是爲什麼你可能會想象 它「直接輸出可執行文件」而無需鏈接。它沒有。 您可以通過傳遞'-c' 選項來指示'gcc' * not *來調用鏈接器。嘗試以這種方式構建程序。 –