这编译得很好
#include <math.h>
int main(void) {
double i = sqrt(9.0);
}
如果我将 9.0 更改为 -9.0,那么我的编译器 (GNU C) 会给出关于“sqrt”的未定义引用的错误。
我期待 sqrt 函数返回 NaN 或异常。C 库如何只为非负参数定义 sqrt?
发生这种情况是因为可以在优化过程中gcc
使用内置函数sqrt
来计算某些函数,包括在许多但不是所有情况下的编译时。如果是这种情况,它将不需要发出对 的调用sqrt
,因此不需要链接到libm
.
gcc 文档有一个完整的内置列表,上面写着(强调我的):
其余函数用于优化目的。
GCC 包括标准 C 库中许多函数的内置版本。即使您指定了 -fno-builtin 选项,带有 _ builtin前缀的版本始终被视为与 C 库函数具有相同的含义。(请参阅 C 方言选项)其中许多功能仅在某些情况下进行了优化;如果它们在特定情况下未优化,则会发出对库函数的调用。
如果我编译这段代码gcc -S
并在我们使用时查看发出的程序集(实时示例),sqrt(9.0)
那么gcc
将使用内置函数并且根本不会发出对sqrt的调用,因为它将在编译时计算它。如果我们更改代码以使用sqrt(-9.0)
它现在将发出(现场示例):
call sqrt
这将需要与 libm 链接,解决方法是添加-lm
到编译选项的末尾。
在sqrt(-9)
我们有域错误的情况下,如果转到 C99 草案标准部分7.12.7.5
sqrt 函数第2段说(强调我的):
sqrt 函数计算 x 的非负平方根。如果参数小于零,则会发生域错误。
如果我们回到7.12.1
错误条件的处理,它会说:
[...] 出现域错误时,该函数返回一个实现定义的值;如果整数表达式math_errhandling & MATH_ERRNO非零,则整数表达式 errno 获取值EDOM;如果整数表达式math_errhandling & MATH_ERREXCEPT不为零,则引发“无效”浮点异常。
因此将设置errno和/或引发浮点异常,这似乎是一种gcc
决定优化效率不高的情况。
很可能,您忘记了链接libm
。更改命令行:
gcc -o foo2 foo2.c -lm
我怀疑根据参数的类型,sqrt()
要么扩展为内联指令,要么扩展为库调用。
//gcc -std=c99 csqrt.c -lm
#include <stdio.h>
#include <complex.h>
#include <math.h>
void print_complex(double complex c){
double i = cimag(c);
printf("%g %c %gi",
creal(c),
i >= 0. ? '+'
: '-',
fabs(i));
}
int main(){
double complex z = csqrt(-9.0);
print_complex(z);//0 + 3i
}