2016-09-28 197 views
6

我正在閱讀一本名爲「GCC簡介」的書,希望有所澄清。該書表明,下面的代碼會導致錯誤但是當我編譯,它構建並運行完美:與靜態庫鏈接的gcc

#include <math.h> 
#include <stdio.h> 
int main (void) { 
    double x = sqrt (2.0); 
    printf ("The square root of 2.0 is %f\n", x); 
    return 0; 
} 

我引用這本書的「試圖創建單獨從這個源文件的可執行使編譯器在鏈接階段給出錯誤信息:

$ gcc -Wall calc.c -o calc 
/tmp/ccbR6Ojm.o: In function `main': 
/tmp/ccbR6Ojm.o(.text+0x19): undefined reference 
    to `sqrt' 

的這本書給出的解決方案是,你應該包括路徑數學庫」libm.a」如下:

$ gcc -Wall calc.c /usr/lib/libm.a -o calc 

這將是非常不方便,必須指定要建立在我們在我們的程序中使用的庫的路徑。我可以理解將路徑添加到我自己的自定義庫的原因,但libm.a已內置到gcc中。雖然這本書是相當古老的(2004年出版),與更現代版本的gcc有什麼不同,因此我們不需要包含路徑爲libm.a

* UPDATE *

我注意到,通過taskinoor給出的答案應符合更新的要求,我用的是-lm標誌,如果()傳遞給開方值在編譯時已知代碼。

我學會了使用VS的C/C++,但我現在的目標是學習和使用gcc。我有Visual Studio 2013和VS編譯器/鏈接器似乎不太挑剔。例如,我可以編譯幾乎任何簡單的程序,而不必指定神祕的編譯器標誌。

我對gcc版本5.4隨KUBUNTU 16.04.1

+4

首先,嘗試一本不同的書籍,因爲幾十年來,鏈接數學庫的標準方法是使用-lm而不是庫的完整路徑。其次,https://gcc.gnu.org/releases.html包含發佈更改。 –

+1

@JohnGriffin:來自同一本書:*「爲了避免在命令行中指定長路徑,編譯器提供了一個快捷選項'-l'來鏈接庫。例如,下面的命令: ' $ gcc -Wall calc.c -lm -o calc'等價於原始命令...「* –

+0

請注意,這些日子的默認值是鏈接動態庫,而不是靜態庫。也許你想問的是與*標準庫鏈接? – Barmar

回答

6

sqrt (2.0);

現代GCC是完全能夠確定你正在努力尋找一個不變的平方根,因此它是能夠在編譯時計算出來。您的目標代碼不包含對sqrt的實際調用。

如果您在運行時使用通過scanf輸入的變量,那麼在沒有libm時它將不會鏈接。

int main() { 
    double x; 

    scanf("%lf", &x); 
    printf("%lf\n", sqrt(x)); 

    return 0; 
} 

沒有libm Ubuntu 14上的gcc 4.8.4。04這個結果:

/tmp/ccVO2fRY.o: In function `main': 
sqrt.c:(.text+0x2c): undefined reference to `sqrt' 
collect2: error: ld returned 1 exit status 

但如果我把而非x喜歡你的例子恆定則沒有libm鏈接罰款。

P.S:我不知道GCC能做到這一點的確切版本。希望別人能指出這一點。

+0

有趣...我必須檢查出它... –

+0

我在Linux上也得到這個錯誤,但在OS X上沒有錯誤。 – Barmar

+0

@Barmar no在OS X上的鏈接器錯誤與編譯時無法解析的變量?對不起,我不知道有關OS X的信息。可能還有其他事情正在發生。 – taskinoor

2

學習我已經注意到,在某些操作系統中,常見的庫可沒有明確的鏈接。特別是,我經常使用最初在我的Mac上開發的C項目,並且在我直接與我使用的庫(如libm)進行鏈接之前,該項目不會在Linux上編譯。

當然,這通常是動態的而不是靜態鏈接...