使用 sqrt 函数时的链接器问题(C、gcc)
Linker problem when using sqrt function (C, gcc)
当我尝试编译下面的最小示例时遇到一个奇怪的问题。第一次调用 sqrt
没有问题,但第二次调用会引发一个我不理解的链接器错误,因为我在调用 gcc
时确实指定了数学库。当我评论第二行时,它 compiles/links 正确。
代码如下:
// File wtf_sqrt.c
#include <math.h>
int main (int argc, char *argv[]) {
int x = 3; // Just an int...
sqrt(3); // This line works fine
sqrt(x); // But this one seems to give the linker trouble. Why?
return 0;
}
这是我的编译命令:
gcc -lm -o wtf_sqrt wtf_sqrt.c
这是返回的错误:
/tmp/ccgQN7y7.o: In function `main':
wtf_sqrt.c:(.text+0x1c): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
我在 Ubuntu 18.04 LTS 上使用 gcc
版本 7.5.0。 libc6-dev
已安装(证据是,当我在 main
的第一行中使用它时,sqrt
有效)。代码是用gedit
写的,所以应该不是空字符的问题。不确定我还能给你什么其他信息......
在这一点上,我真的怀疑这是我的 config/distribution 的问题,但我需要一些外部建议。
问题可能出在 sqrt()
所采用的参数中。这是 math.h
header:
中的函数
_Check_return_ _CRT_JIT_INTRINSIC double __cdecl sqrt(_In_ double _X);
如您所见,函数采用的参数类型是double。在您的第一个 sqrt()
调用中,您只需在括号内键入值 3 即可传递值 - 编译器可能会将此 3 视为双精度值并成功编译。但是在第二次调用中,您传递了一个参数 x
,它是一个 int
(并且您没有将其转换为 double
)。由于 C 不支持函数重载,编译器找不到带有 int
参数的 sqrt
函数并弹出错误。将 x
转换为 double 应该可以解决问题:
sqrt((double)x);
正如用户 aragaer、Weather Vane 和 Tom Karzes 在评论中所建议的那样,问题出在编译命令中 -lm
的位置。我错误地认为首先提供它是正确的方法,因为那时链接器可能知道在哪里可以找到该函数,但我应该最后提供它以便链接器已经知道我需要 sqrt
从我的程序.事实上,重要的不仅是 -l
选项的顺序,还有(要)编译的文件的顺序。所以正确的命令是:
gcc -o wtf_sqrt wtf_sqrt.c -lm
正如用户 Weather Vane 所解释的,对 sqrt
的第一次调用可能由于优化而被替换或删除,因此不会产生错误。
这个问题实际上有两个部分,每个部分都是前面问题的重复。
当使用常量参数调用时 sqrt
不会导致 link 错误的事实是编译器在编译期间对其进行评估,如回答 here, here, and .
即使 -lm
包含在 link 中也会出现 link 错误的事实是 -lm
必须列在使用数学库的模块之后, 正如回答 here, here, here, and here.
当我尝试编译下面的最小示例时遇到一个奇怪的问题。第一次调用 sqrt
没有问题,但第二次调用会引发一个我不理解的链接器错误,因为我在调用 gcc
时确实指定了数学库。当我评论第二行时,它 compiles/links 正确。
代码如下:
// File wtf_sqrt.c
#include <math.h>
int main (int argc, char *argv[]) {
int x = 3; // Just an int...
sqrt(3); // This line works fine
sqrt(x); // But this one seems to give the linker trouble. Why?
return 0;
}
这是我的编译命令:
gcc -lm -o wtf_sqrt wtf_sqrt.c
这是返回的错误:
/tmp/ccgQN7y7.o: In function `main':
wtf_sqrt.c:(.text+0x1c): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
我在 Ubuntu 18.04 LTS 上使用 gcc
版本 7.5.0。 libc6-dev
已安装(证据是,当我在 main
的第一行中使用它时,sqrt
有效)。代码是用gedit
写的,所以应该不是空字符的问题。不确定我还能给你什么其他信息......
在这一点上,我真的怀疑这是我的 config/distribution 的问题,但我需要一些外部建议。
问题可能出在 sqrt()
所采用的参数中。这是 math.h
header:
_Check_return_ _CRT_JIT_INTRINSIC double __cdecl sqrt(_In_ double _X);
如您所见,函数采用的参数类型是double。在您的第一个 sqrt()
调用中,您只需在括号内键入值 3 即可传递值 - 编译器可能会将此 3 视为双精度值并成功编译。但是在第二次调用中,您传递了一个参数 x
,它是一个 int
(并且您没有将其转换为 double
)。由于 C 不支持函数重载,编译器找不到带有 int
参数的 sqrt
函数并弹出错误。将 x
转换为 double 应该可以解决问题:
sqrt((double)x);
正如用户 aragaer、Weather Vane 和 Tom Karzes 在评论中所建议的那样,问题出在编译命令中 -lm
的位置。我错误地认为首先提供它是正确的方法,因为那时链接器可能知道在哪里可以找到该函数,但我应该最后提供它以便链接器已经知道我需要 sqrt
从我的程序.事实上,重要的不仅是 -l
选项的顺序,还有(要)编译的文件的顺序。所以正确的命令是:
gcc -o wtf_sqrt wtf_sqrt.c -lm
正如用户 Weather Vane 所解释的,对 sqrt
的第一次调用可能由于优化而被替换或删除,因此不会产生错误。
这个问题实际上有两个部分,每个部分都是前面问题的重复。
当使用常量参数调用时 sqrt
不会导致 link 错误的事实是编译器在编译期间对其进行评估,如回答 here, here, and
即使 -lm
包含在 link 中也会出现 link 错误的事实是 -lm
必须列在使用数学库的模块之后, 正如回答 here, here, here, and here.