[英]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.