对于以下 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(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)
请注意,这个问题是关于常量折叠,但suggested duplicate question是关于缺少链接库的。
答案 0 :(得分:59)
gcc
可以使用builtin functions,他们的文档说:
其中许多功能仅在某些情况下得到优化;如果他们 没有在特定情况下优化,调用库函数 被发射出来。
所以因此gcc
在使用内置函数时不需要链接数学库,但由于log(0)
是not defined,它可能会强制gcc
在运行时对其进行评估 - 因为它有副作用。
如果我们查看 4 段落中的draft C99 standard部分7.12.1
错误情况处理,那就说明了(强调我的 EM>):
如果数学的大小,浮动结果会溢出 结果是有限的,但是很大,数学结果不可能 表示在一个对象中没有非常的舍入误差 指定的类型。如果浮动结果溢出并且默认舍入为 实际上,或者如果数学结果是精确的无穷大 有限参数(例如log(0.0)),然后函数返回 宏的值HUGE_VAL,HUGE_VALF或HUGE_VALL根据 返回类型,与函数的正确值具有相同的符号; 如果是整数表达式math_errhandling&amp; MATH_ERRNO非零, 整数表达式errno获取值ERANGE; 如果是整数 表达式math_errhandling&amp; MATH_ERREXCEPT非零, 如果是,那么''除零''浮点异常会被提出 数学结果是一个精确的无穷大和'溢出'' 否则会引发浮点异常。
我们可以从实时示例中看到使用-S
标志来生成汇编,grep log
来过滤掉对log
的调用。
如果log(0.0)
生成以下指令( see it live ):
call log
但是在log(10.0)
的情况下,生成了call log
指令,( see it live )。
我们通常可以通过使用-fno-builtin flag阻止gcc
使用内置函数,这可能是测试内置函数是否被使用的更快方法。
请注意-lm
needs to go after the source file,例如(取自链接的答案),如果main.c
需要数学库,那么您将使用:
gcc main.c -lm
答案 1 :(得分:8)
编译没问题,只是缺少链接器开关-lm
。
第二个版本可能会编译和链接,因为gcc
用常量替换log(10.0)
,因此不需要调用数学库。在第二种情况下,结果在数学上是未定义的,并且评估会导致域错误。在这种情况下,表达式不能被常量替换,因为域错误的处理在运行时可能会有所不同。
引用C标准(draft):
在域错误上,该函数返回一个实现定义的 值;如果是整数表达式math_errhandling&amp; MATH_ERRNO是 非零,整数表达式errno获取值EDOM;如果 整数表达式math_errhandling&amp; MATH_ERREXCEPT非零, 提出''无效''浮点异常。
因此,log(0.0)
的评估会导致返回值HUGE_VAL
(不是我之前声明的NAN
)或浮点异常。
编辑:我根据收到的评论更正了我的答案,并添加了C标准中描述的链接。