对“只有某些math.h函数”的未定义引用

12

我遇到了一个奇怪的问题。

数学库已经添加到我的makefile中。

# include standard C library
LDFLAGS += -lc
# include standard math library
LDFLAGS += -lm

在输出文件(.map)中,我可以看到所有内容都已经正确地链接起来:

LOAD c:/gnu/powerpc-eabi/3pp.ronetix.powerpc-eabi/bin/../lib/gcc/powerpc-eabi/4.3.3/nof\libgcc.a
LOAD c:/gnu/powerpc-eabi/3pp.ronetix.powerpc-eabi/bin/../lib/gcc/powerpc-eabi/4.3.3/../../../../powerpc-eabi/lib/nof\libc.a
LOAD c:/gnu/powerpc-eabi/3pp.ronetix.powerpc-eabi/bin/../lib/gcc/powerpc-eabi/4.3.3/../../../../powerpc-eabi/lib/nof\libm.a

当我执行时
z = pow((double) 2, (double) 3);

它能正常工作。但是如果我测试另一个函数,例如:

double result = asin(x);

我将会得到:

undefined reference to `asin'
collect2: ld returned 1 exit status

这怎么可能呢?在 math.h 中都有 powasin,请看下面的代码:
/* Non reentrant ANSI C functions.  */

#ifndef _REENT_ONLY
#ifndef __math_6881
extern double acos _PARAMS((double));
extern double asin _PARAMS((double));
extern double atan2 _PARAMS((double, double));
extern double cosh _PARAMS((double));
extern double sinh _PARAMS((double));
extern double exp _PARAMS((double));
extern double ldexp _PARAMS((double, int));
extern double log _PARAMS((double));
extern double log10 _PARAMS((double));
extern double pow _PARAMS((double, double));
extern double sqrt _PARAMS((double));
extern double fmod _PARAMS((double, double));
#endif /* ! defined (__math_68881) */
#endif /* ! defined (_REENT_ONLY) */

为什么一个可以工作而另一个会生成链接问题? 如果我在libm.a上运行-nm,将得到以下结果:(抱歉,输出结果很多,我只复制了带有sin一词的部分)

lib_a-e_asin.o:
         U __adddf3
         U __divdf3
         U __gtdf2
00000000 T __ieee754_asin
         U __ieee754_sqrt
         U __muldf3
         U __subdf3
         U fabs

lib_a-e_j0.o:
         U __adddf3
         U __divdf3
         U __gtdf2
00000470 T __ieee754_j0
         U __ieee754_log
         U __ieee754_sqrt
000009b8 T __ieee754_y0
         U __ltdf2
         U __muldf3
         U __subdf3
         U cos
         U fabs
000000b0 r pR2
00000108 r pR3
00000058 r pR5
00000000 r pR8
000000e0 r pS2
00000138 r pS3
00000088 r pS5
00000030 r pS8
00000004 t pzero
00000220 r qR2
00000280 r qR3
000001c0 r qR5
00000160 r qR8
00000250 r qS2
000002b0 r qS3
000001f0 r qS5
00000190 r qS8
00000218 t qzero
         U sin

lib_a-e_j1.o:
         U __adddf3
         U __divdf3
         U __gtdf2
00000470 T __ieee754_j1
         U __ieee754_log
         U __ieee754_sqrt
00000950 T __ieee754_y1
         U __muldf3
         U __subdf3
         U cos
         U fabs
00000004 t pone
000000b0 r pr2
00000108 r pr3
00000058 r pr5
00000000 r pr8
000000e0 r ps2
00000138 r ps3
00000088 r ps5
00000030 r ps8
00000218 t qone
00000220 r qr2
00000280 r qr3
000001c0 r qr5
00000160 r qr8
00000250 r qs2
000002b0 r qs3
000001f0 r qs5
00000190 r qs8
         U sin

lib_a-e_jn.o:
         U __adddf3
         U __divdf3
         U __floatsidf
         U __gedf2
         U __gtdf2
         U __ieee754_j0
         U __ieee754_j1
00000434 T __ieee754_jn
         U __ieee754_log
         U __ieee754_sqrt
         U __ieee754_y0
         U __ieee754_y1
00000000 T __ieee754_yn
         U __ltdf2
         U __muldf3
         U __subdf3
         U cos
         U fabs
         U sin


lib_a-e_sinh.o:
         U __adddf3
         U __divdf3
         U __gtdf2
         U __ieee754_exp
00000000 T __ieee754_sinh
         U __muldf3
         U __subdf3
         U expm1
         U fabs


lib_a-ef_asin.o:
         U __addsf3
         U __divsf3
         U __gtsf2
00000000 T __ieee754_asinf
         U __ieee754_sqrtf
         U __mulsf3
         U __subsf3
         U fabsf


lib_a-ef_j0.o:
         U __addsf3
         U __divsf3
         U __gtsf2
0000035c T __ieee754_j0f
         U __ieee754_logf
         U __ieee754_sqrtf
000006cc T __ieee754_y0f
         U __ltsf2
         U __mulsf3
         U __subsf3
         U cosf
         U fabsf
00000058 r pR2
00000084 r pR3
0000002c r pR5
00000000 r pR8
00000070 r pS2
0000009c r pS3
00000044 r pS5
00000018 r pS8
00000004 t pzerof
00000110 r qR2
00000140 r qR3
000000e0 r qR5
000000b0 r qR8
00000128 r qS2
00000158 r qS3
000000f8 r qS5
000000c8 r qS8
000001a0 t qzerof
         U sinf

lib_a-ef_j1.o:
         U __addsf3
         U __divsf3
         U __gtsf2
0000031c T __ieee754_j1f
         U __ieee754_logf
         U __ieee754_sqrtf
0000062c T __ieee754_y1f
         U __mulsf3
         U __subsf3
         U cosf
         U fabsf
00000004 t ponef
00000058 r pr2
00000084 r pr3
0000002c r pr5
00000000 r pr8
00000070 r ps2
0000009c r ps3
00000044 r ps5
00000018 r ps8
000001a0 t qonef
000000b0 r qr2
000000e0 r qr8
000000c8 r qs2
000000f8 r qs8
         U sinf

lib_a-ef_sinh.o:
         U __addsf3
         U __divsf3
         U __gtsf2
         U __ieee754_expf
00000000 T __ieee754_sinhf
         U __mulsf3
         U __subsf3
         U expm1f
         U fabsf

lib_a-er_lgamma.o:
         U __adddf3
         U __divdf3
         U __eqdf2
         U __fixdfsi
         U __floatsidf
00000004 T __ieee754_lgamma_r
         U __ieee754_log
         U __kernel_cos
         U __kernel_sin
         U __ltdf2
         U __muldf3
         U __nedf2
         U __subdf3
         U fabs
         U floor


lib_a-erf_lgamma.o:
         U __addsf3
         U __divsf3
         U __eqsf2
         U __fixsfsi
         U __floatsisf
00000004 T __ieee754_lgammaf_r
         U __ieee754_logf
         U __kernel_cosf
         U __kernel_sinf
         U __ltsf2
         U __mulsf3
         U __nesf2
         U __subsf3
         U fabsf
         U floorf

lib_a-k_sin.o:
         U __adddf3
         U __fixdfsi
00000000 T __kernel_sin
         U __muldf3
         U __subdf3

lib_a-kf_sin.o:
         U __addsf3
         U __fixsfsi
00000000 T __kernel_sinf
         U __mulsf3
         U __subsf3

lib_a-s_asinh.o:
         U __adddf3
         U __divdf3
         U __gtdf2
         U __ieee754_log
         U __ieee754_sqrt
         U __muldf3
00000000 T asinh
         U fabs
         U log1p

lib_a-s_cos.o:
         U __ieee754_rem_pio2
         U __kernel_cos
         U __kernel_sin
         U __subdf3
00000000 T cos

lib_a-s_isinf.o:
00000000 T isinf

lib_a-s_isinfd.o:
00000000 T __isinfd

lib_a-s_sin.o:
         U __ieee754_rem_pio2
         U __kernel_cos
         U __kernel_sin
         U __subdf3
00000000 T sin

lib_a-sf_asinh.o:
         U __addsf3
         U __divsf3
         U __gtsf2
         U __ieee754_logf
         U __ieee754_sqrtf
         U __mulsf3
00000000 T asinhf
         U fabsf
         U log1pf

lib_a-sf_cos.o:
         U __ieee754_rem_pio2f
         U __kernel_cosf
         U __kernel_sinf
         U __subsf3
00000000 T cosf

lib_a-sf_isinf.o:
00000000 T isinff

lib_a-sf_isinff.o:
00000000 T __isinff

lib_a-sf_sin.o:
         U __ieee754_rem_pio2f
         U __kernel_cosf
         U __kernel_sinf
         U __subsf3
00000000 T sinf

lib_a-w_asin.o:
         U __errno
         U __fdlib_version
         U __gtdf2
         U __ieee754_asin
         U __isnand
00000004 T asin
         U fabs
         U matherr
         U nan

lib_a-w_sincos.o:
         U cos
         U sin
00000000 T sincos

lib_a-w_sinh.o:
         U __errno
         U __fdlib_version
         U __gtdf2
         U __ieee754_sinh
         U finite
         U matherr
00000004 T sinh

lib_a-wf_asin.o:
         U __errno
         U __extendsfdf2
         U __fdlib_version
         U __gtsf2
         U __ieee754_asinf
         U __truncdfsf2
00000004 T asinf
         U fabsf
         U isnanf
         U matherr
         U nan

lib_a-wf_sincos.o:
         U cosf
00000000 T sincosf
         U sinf

lib_a-wf_sinh.o:
         U __errno
         U __extendsfdf2
         U __fdlib_version
         U __gtsf2
         U __ieee754_sinhf
         U __truncdfsf2
         U finitef
         U matherr
00000004 T sinhf

编辑1:我进行了更多测试,问题如下(与我上面最初陈述的不同):

double aa;
double bb = 1.0;
double cc;
aa = sin(1.0);
cc = sin (bb);

在构建时发生的情况是,我在最后一行收到一个'未定义的引用'错误,这意味着当我使用常量时,它是正常的,但是当我将变量传递给sin函数时,它就不能链接。我还测试了许多其他数学函数,会得到完全相同的链接器问题。一旦我将变量传递给数学函数,我就无法再链接了。有什么想法吗?

2
如果您使用平台工具列出对象文件的符号表(例如nm),那么libm.a中缺失的函数是否被列出?该错误指示问题位于链接阶段,在该阶段头文件不再涉及。换句话说,math.h的内容不会影响此问题。 - outis
1
检查你的LD_LIBRARY_PATH。你可能正在查看与链接器使用的库不同的库。请记住,头文件math.h也可能引用与被选中的库不同的库。 - cdarke
2
你能否只使用一个源文件来重现问题,而不需要任何makefile来构建可执行文件,例如:gcc -o test test.c -lm或者gcc -static -o test test.c -lm - n. m.
1
@hexa:那会改变编译器的行为,而不是库的内容。方言选择可能会删除头文件的某些部分,但这会导致编译器错误而不是链接器错误。 - Clifford
3
如果您正在进行优化编译,编译器可以优化运行时对 sin(1.0) 的调用并替换为在编译时计算的常数。请问您是否已经包含了 <math.h> 头文件(请参考乔纳森的回答)? - David Hammen
显示剩余9条评论
4个回答

10
链接器没有对pow((double) 2, (double) 3) 提出异议,因为编译器将其替换为常量8.0。 您不应依赖此行为,而应始终正确使用-lm选项。(顺便说一句,这更清楚地写为pow(2.0, 3.0)
考虑以下程序:
#include <stdio.h>
#include <math.h>
int main(void) {
    double x = 0.1;
    printf("%g\n", pow(2.0, 3.0));
    printf("%g\n", asin(x));
    return 0;
}

当我在我的系统上编译和链接它时使用

gcc c.c -o c

我得到:

/tmp/ccXx8ZRL.o: In function `main':
c.c:(.text+0x36): undefined reference to `asin'
collect2: ld returned 1 exit status
请注意,它抱怨asin但不是pow
如果我将pow调用更改为pow(x, 3.0),我会得到:
/tmp/ccOeSaBK.o: In function `main':
c.c:(.text+0x24): undefined reference to `pow'
c.c:(.text+0x52): undefined reference to `asin'
collect2: ld returned 1 exit status

通常情况下,如果您想调用标准数学库函数,您需要在源文件顶部包含#include <math.h>(我假设您已经有了),并且您需要在编译器中传递-lm选项 在需要它的文件之后。(链接器会跟踪尚未解析的引用,因此它需要先看到引用asin的对象文件,以便在看到数学库时解析它。)

链接器没有关于对pow(2.0, 3.0)的调用的投诉,因为gcc足够聪明,可以将其解析为常量8.0。编译生成的目标文件中没有对pow函数的调用,因此链接器不需要解析它。如果我将pow(2.0, 3.0)更改为pow(x, 3.0),编译器不知道结果会是什么,因此会生成调用。


10

-lm -lc -lgcc 的顺序非常重要。只有这个顺序对我有效。

这些命令是用于链接选项的!


3
-lclgcc选项通常是不必要的。有哪些环境需要它们? - Keith Thompson

8

你是否在所有地方都包含了 <math.h>

请注意库中的名称都以 __ieee754_ 为前缀,但链接器无法找到的那些名称没有。

当您编译此代码时会发生什么?

#include <math.h>

int main(void)
{
    double d = pow(2, 3);
    double e = asin(1.0 / d);
    return (int)(e+1);
}

如果文件名为 mathtest.c,则使用以下命令进行编译:
gcc -o mathtest mathtest.c -lm

(考虑到这个编译失败了,mathtest.o中定义了哪些符号?)

我在主问题下添加了一条评论:

你使用的是哪个平台?使用的是哪个C编译器?你在进行交叉编译吗?执行链接的命令行是什么?(我看到DOS / Windows C:路径和PowerPC架构。)有没有可能你正在使用用于类型通用数学的math.h库?

从您提供的LOAD路径来看,我看到:

LOAD c:/gnu/powerpc-eabi/3pp.ronetix.powerpc-eabi/bin/../lib/gcc/powerpc-eabi/4.3.3/../../../../powerpc-eabi/lib/nof\libm.a

我认为可以简化为以下内容:

LOAD c:/gnu/powerpc-eabi/3pp.ronetix.powerpc-eabi/powerpc-eabi/lib/nof\libm.a

我感兴趣的路径中的一部分是 nof 部分; 这可能是指 'no floating point'吗?另一个让我非常感兴趣的部分是带有 c: 前缀的 powerpc 的存在;它似乎是在 Windows 平台上进行 PowerPC 的交叉编译。对于这些事情,保持坦率和明确非常重要;我们需要这样的信息才能帮助您合理地解决问题。

你测试的是 libm.a 库吗,还是试验了另一个文件?


1
“未定义的引用”是一个链接器错误,而不是编译器错误。未包含头文件会导致编译器错误(或在C89中警告)。 - Clifford
5
除非将头文件中的代码名称asin映射为__ieee574_asin,否则将包含头文件。 从 nm 输出的结果显示一个符号__ieee574_asin。如果头文件按建议映射了名称,则在没有头文件的情况下调用该函数会意味着调用了“错误”的函数。如果我提供的示例代码链接了,但原帖的代码没有链接,我倾向于认为这是“案例证明”。是的,你可以合理地争论说你应该能够写extern double asin(double);并且它应该能工作,但我以前见过这种名称映射的情况。 - Jonathan Leffler
ISO/IEC 9899:1999 (E) §7.1.4/2:只要库函数可以在不引用头文件中定义的任何类型的情况下声明,也可以声明该函数并在不包括其相关头文件的情况下使用它。因此,如果需要包含,则实现是不符合标准的。 - bdonlan
@bdonlan:我从未说过其他的话 - 我也知道标准中的那一部分。但是我以前在(Linux)实现中看到过这种游戏。这也是为什么包含系统头文件比猜测系统更好的一个(主要)原因。但是,如果我的示例无法干净地编译,那么该实现就是有问题的。如果它可以干净地编译,但 OP 的代码不能,那么 OP 的代码做了一些类似于不包括 <math.h> 的事情,而这个实现需要它... 包括 <math.h> 的修复方法将在任何地方都有效。 - Jonathan Leffler
@alborz:好的 - 如果我展示的代码无法链接,那么你需要修复你的编译器、你的头文件或者你的库。当前的设置是有问题的。我无法确定问题是否出现在你的头文件或者库中,或者你在/etc/ld.so.conf或等效位置设置了一些非常奇怪的东西(因此它会引入错误的数学库)。此外,通常情况下,我希望有一个libm.so而不是libm.a;我不知道这是否是一个因素。最后(暂时),你是否检查过标准集合中是否有其他缺失的符号? - Jonathan Leffler
显示剩余3条评论

1
你可以使用 "filename.c -lm" 来解决这个问题。请不要忘记使用头文件 math.h。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接