簡體   English   中英

log(10.0)可以編譯但是log(0.0)不能用未定義的引用嗎?

[英]log(10.0) can compile but log(0.0) cannot with undefined reference?

對於以下C源代碼:

#include <math.h>

int main(void)
{
    double          x;

    x = log(0.0);

    return 0;
}

當我用gcc -lm編譯時,我得到了:

/tmp/ccxxANVH.o: In function `main':
a.c:(.text+0xd): undefined reference to `log'
collect2: error: ld returned 1 exit status

但是,如果我用log(10.0)替換log(0.0) log(10.0) ,那么它可以成功編譯。

我不太明白這一點,因為無論它們是否具有數學意義,它們都應該編譯 - 沒有語法錯誤。 有人能解釋一下嗎?

以防萬一,我的gcc -v輸出:

Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)

請注意,這個問題是關於常量折疊,但建議的重復問題是關於缺少鏈接庫。

gcc可以在很多情況下使用內置函數 ,他們的文檔說:

其中許多功能僅在某些情況下進行了優化; 如果它們在特定情況下未被優化,則會發出對庫函數的調用。

因此,當使用內置函數時, gcc不需要鏈接數學庫,但由於沒有定義 log(0)它可能會強制gcc在運行時對其進行評估,因為它有副作用。

如果我們看一下C99標准草案7.12.1草案中第4段中的錯誤條件的處理,則說明( 強調我的 ):

如果數學結果的大小是有限的,則浮動結果溢出,但是如果在指定類型的對象中沒有非常的舍入誤差,則無法表示數學結果。 如果浮點結果溢出且默認舍入有效,或者數學結果是有限參數的精確無窮大(例如log(0.0)),則該函數返回宏HUGE_VAL,HUGE_VALF或HUGE_VALL的值,具體取決於返回類型 ,與函數的正確值具有相同的符號; 如果整數表達式math_errhandling&MATH_ERRNO非零,則整數表達式errno獲取值ERANGE; 如果整數表達式math_errhandling&MATH_ERREXCEPT非零,則如果數學結果是精確無窮大,則會引發''除零''浮點異常,否則會引發''溢出''浮點異常。

我們可以從實例看到使用-S標志來生成程序集和grep log以過濾掉對log調用。

log(0.0)的情況下,生成以下指令( 請參見實時 ):

call    log

但是在log(10.0)的情況下,沒有生成call log指令( 請參見實時 )。

我們通常可以通過使用-fno-builtin標志來防止gcc使用內置函數,這可能是測試是否正在使用內置函數的更快方法。

請注意, -lm 需要在源文件之后 ,例如( 取自鏈接的答案 ),如果main.c需要數學庫,那么您將使用:

 gcc main.c -lm 

編譯沒問題,只是缺少鏈接器開關-lm

第二個版本可能編譯和鏈接,因為gcc用常量替換log(10.0) ,因此不需要調用數學庫。 在第二種情況下,結果在數學上是未定義的,並且評估會導致域錯誤。 在這種情況下,表達式不能被常量替換,因為域錯誤的處理在運行時可能會有所不同。

引用C標准( 草案 ):

在域錯誤上,該函數返回一個實現定義的值; 如果整數表達式math_errhandling&MATH_ERRNO非零,則整數表達式errno獲取值EDOM; 如果整數表達式math_errhandling&MATH_ERREXCEPT非零,則引發''invalid''浮點異常。

因此,對log(0.0)評估要么返回值HUGE_VAL (不是我之前聲明的NAN )或浮點異常。

編輯:我根據收到的評論更正了我的答案,並添加了C標准中描述的鏈接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM