undefined reference to xxx 问题总结
GCC编译过程
- 预处理Pre-Processing;宏展开等,可以生成.i文件
- 编译Compiling(狭义的编译);进行语法分析,转换成汇编语言,生成.s文件
- 汇编Assembling;把汇编语言代码转换成目标代码,生成.o文件
- 连接Linking;连接器ld将as创建的目标文件转换为可执行文件,生成a.out文件
代码的基本语法与格式错误一般是在前几个阶段被发现的, 在最后一步即连接时才会报错 undefined reference to ,即找不到符号的定义;
找不到符号定义的几种情况
链接时确实没有指定包含符号定义的相关文件;
- 链接时没有指定包含符号定义的源文件(
.c ,.cpp ); - 链接时没有指定包含符号定义源文件生成的目标文件(
.o ); - 链接时没有指定包含符号定义的库文件(
.so ,.a );
链接选项的顺序导致
越基础的库的连接选项越要靠后面; 如果 libB.so 又依赖于 libA.so , 则连接选项应该是 -lB, -lA
函数符号修饰导致
- 函数定义和声明不一致,包括参数类型、个数及返回值,甚至其他修饰如const等;
- C和C++混合编程,在应用C语言的库的时候,就要加上
extern "C" ,告诉C++ 编译器按照C 语言的符号修饰规则去找这些符号;
由于C++有符号修饰(Name Decoration)或符号改编(Name Mangling)的机制;导致编译生成的符号表和代码中定义的会有不同; nm -C 可以查看符号修饰之前的名字;
- 编译器版本或者编译器选项不一致,编译库与链接库所使用的版本或选项不一致导致。
编译选项隐藏了接口
在编译库时如果使用了 -fvisibility=hidden 选项将导致其定义的接口对外不可见,通过nm命令导出符号表可以看到其类型不是T; 可以使用 -fvisibility=default 选项来编译动态库,恢复其默认的可见属性。 也可以在接口声明前加上__attribute__ ((visibility ("default"))) 显示声明接口可见。 可以定义成宏定义便于使用,示例如下
#define PUBLIC_API __attribute__ ((visibility ("default")))
#define LOCAL_API __attribute__ ((visibility ("hidden")))
PUBLIC_API void public_api();
LOCAL_API void local_api();
|