2012-07-04 31 views
16

下面是一個小例子,概述了我的問題GCC將不能正確包括math.h中

test.c的:

#include <stdio.h> 
#include <math.h> 

main() 
{ 
    fmod (3, 2); 
} 

這裏是我發出命令編譯test.c

gcc -lm test.c -o test 

這裏是我發出上述命令時得到的輸出

/tmp/ccQmRk99.o: In function `main': 
test.c:(.text+0x3e): undefined reference to `fmod' 
collect2: ld returned 1 exit status 

如果我使用cc,我會得到相同的輸出。我用gcc

gcc-4.6.real (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 

任何想法以下版本,爲什麼我的程序將無法編譯?

+0

此代碼編譯和鏈接我的罰款GCC 4.1.2和4.3.4 ... –

+0

@OliCharlesworth我簡直複製粘貼顯然這一切,和它不適用於我 – puk

+0

@OliCharlesworth是否有可能math.h不包含在內? – puk

回答

46

問題來自鏈接器,ld,而不是gcc(因此退出狀態消息)。通常,ld需要以usersupplier的順序指定對象和庫,其中user是使用庫函數的對象,supplier是提供它的對象。

當您的test.c被編譯爲一個對象中的編譯器指出FMOD是一個未定義的參考

$ gcc -c test.c 
$ nm test.o 
       U fmod 
0000000000000000 T main 

(NM由對象文件列出所提到的所有功能)

接頭改變未定義引用定義的引用,查找引用以查看它們是否在其他文件中提供。

$ gcc -lm test.o 
$ nm a.out 
0000000000600e30 d _DYNAMIC 
0000000000600fe8 d _GLOBAL_OFFSET_TABLE_ 
00000000004006a8 R _IO_stdin_used 
       w _Jv_RegisterClasses 
0000000000600e10 d __CTOR_END__ 
... 
0000000000601018 D __dso_handle 
       w __gmon_start__ 
... 
       U [email protected]@GLIBC_2.2.5 
0000000000601020 A _edata 
0000000000601030 A _end 
0000000000400698 T _fini 
0000000000400448 T _init 
0000000000400490 T _start 
00000000004004bc t call_gmon_start 
0000000000601020 b completed.7382 
0000000000601010 W data_start 
0000000000601028 b dtor_idx.7384 
       U [email protected]@GLIBC_2.2.5 
0000000000400550 t frame_dummy 
0000000000400574 T main 

其中大部分指的是在主要設置環境之前和之後運行的libc函數。您可以看到fmod現在指向glibc,它將由共享庫系統解決。

我的系統默認設置爲使用共享庫。如果我不是強制靜態鏈接我拿到訂單的依賴,你看到

$ gcc -static -lm test.o 
test.o: In function `main': 
test.c:(.text+0x40): undefined reference to `fmod' 
collect2: ld returned 1 exit status 

-lm在後面的鏈接器命令,test.o,使得它能夠成功鏈接。 檢查符號FMOD現在應該解決一個實際的地址,的確是

$ gcc -static test.o -lm 
$ nm a.out | grep fmod 
0000000000400480 T __fmod 
0000000000402b80 T __ieee754_fmod 
0000000000400480 W fmod 
+0

連接過程的一個非常好的解釋,但底線是模糊的。你是否說從'-lm test.o'改爲'test.o -lm'解決了這個問題(因此,他應該將'gcc -lm test.c -o test'改爲'gcc test.c - lm -o test')? – ugoren

+7

是的。除非你有其他特別的理由,否則庫**總是屬於鏈接命令行的末尾**。並且它們屬於依賴性順序,即如果A依賴於B,那麼B必須始終在命令行後面出現A. –

+0

好的答案,我從來不知道「nm」。 –

2

從GCC(1)手冊頁:「-l選項的位置是顯著」

具體做法是:

-llibrary 
    -l library 
     Search the library named library when linking. (The second alternative with the library as a 
     separate argument is only for POSIX compliance and is not recommended.) 

     It makes a difference where in the command you write this option; the linker searches and processes 
     libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z 
     after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be 
     loaded. 

     The linker searches a standard list of directories for the library, which is actually a file named 
     liblibrary.a. The linker then uses this file as if it had been specified precisely by name. 

     The directories searched include several standard system directories plus any that you specify with 
     -L. 

     Normally the files found this way are library files---archive files whose members are object files. 
     The linker handles an archive file by scanning through it for members which define symbols that have 
     so far been referenced but not defined. But if the file that is found is an ordinary object file, it 
     is linked in the usual fashion. The only difference between using an -l option and specifying a file 
     name is that -l surrounds library with lib and .a and searches several directories.