C/C++混合项目,程序运行报错:未定义函数符号

参考

C/C++项目混合编译

extern "C" 详解

《C++ Primer Plus》函数重载篇章

环境

系统:ubuntu20

编译器:gcc

问题

C 和 C++ 源码的混合项目,编译成功,但是程序运行报错找不到函数符号。

背景

项目中使用第三方的代码,是 C 文件,但项目本身是 C++ 工程。

CMakeList.txt 语法检查没有问题,在工程的 devel/lib 文件夹能够找到生成的 so 库。

so 库符号检查

检查报错的 local_planner.cpp 生成的 liblocal_planner.so 文件。查看库有哪些符号丢失。

ldd -r liblocal_planner.so
...

undefined symbol: _Z18dubins_path_lengthP10DubinsPath        (./liblocal_planner.so)
undefined symbol: _Z20dubins_shortest_pathP10DubinsPathPdS1_d        (./liblocal_planner.so)
undefined symbol: _Z18dubins_path_sampleP10DubinsPathdPd        (./liblocal_planner.so)

上面的三个未找到的函数符号中存在第三方库的函数名:dubins_path_length,dubins_shortest_path,dubins_path_sample。

查看第三方代码生成的 libdubins_lib 库中有哪些符号(函数,变量)

nm -D libdubins_lib.so
                 U acos
                 U atan2
                 U cos
                 w __cxa_finalize
0000000000003000 R DIRDATA
0000000000001e01 T dubins_extract_subpath
0000000000001f57 T dubins_intermediate_results
00000000000028c0 T dubins_LRL
000000000000213f T dubins_LSL
00000000000023d5 T dubins_LSR
00000000000015f4 T dubins_path
0000000000001dc0 T dubins_path_endpoint
0000000000001746 T dubins_path_length
0000000000001a40 T dubins_path_sample
0000000000001cf6 T dubins_path_sample_many
0000000000001841 T dubins_path_type
00000000000026ec T dubins_RLR
000000000000256f T dubins_RSL
0000000000002288 T dubins_RSR
0000000000001856 T dubins_segment
00000000000017be T dubins_segment_length
0000000000001806 T dubins_segment_length_normalized
00000000000013ff T dubins_shortest_path
0000000000002a9e T dubins_word
                 U floor
                 U fmin
0000000000001399 T fmodr
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
00000000000013d2 T mod2pi
                 U sin
                 U sqrt
                 U __stack_chk_fail

第三方库 so 文件存在函数符号:dubins_path_length,dubins_shortest_path,dubins_path_sample。且函数符号与函数名一致。

经过对比可以发现,第三方库生成的 libdubins_lib.so 文件中函数符号确实和使用第三方函数的库 liblocal_planner.so 中的函数符号不相同。这也就是为什么程序运行中会找不到函数符号的直接原因。

extern "C"

怀疑是 C 和 C++ 项目混合编译存在问题。搜索后了解到在 C++ 项目中使用 C 源码,是需要在第三方代码的 C 头文件中增加 extern "C" 宏指令。

#ifdef __cplusplus
extern "C" {
#endif

// c 头文件内容
...


#ifdef __cplusplus
}
#endif

补充宏指令后编译,检查 so 库已经没有那三个函数符号未定义的提示了!

C/C++ 的函数符号规则

当编译的程序是 C++ 工程时候,会定义 __cplusplus 宏,提供给编译器识别。(很多编译器,系统环境变量名都是以两个下划线开头 “__XXX”,所以尽量不要定义两个下划线开头的变量,避免覆盖编译器,环境变量。)

extern 是 C/C++ 语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
被 extern "C" 修饰的变量和函数是按照 C 语言方式编译和连接的。

第三方库的函数原型如下:

double dubins_path_length(DubinsPath* path);
int dubins_path_sample(DubinsPath* path, double t, double q[3]);
int dubins_shortest_path(DubinsPath* path, double q0[3], double q1[3], double rho);

由于在 C 中,全局函数名唯一,预处理后的函数符号直接使用函数名即可。所以在其 so 库中,这三个函数符号就是函数名。

0000000000001746 T dubins_path_length
0000000000001a40 T dubins_path_sample
00000000000013ff T dubins_shortest_path

而在未添加 extern "C" 之前,在 C++ 工程中,默认这三个函数都是 C++ 的函数,它们会被处理为 C++ 风格的函数符号。

_Z18dubins_path_lengthP10DubinsPath
_Z20dubins_shortest_pathP10DubinsPathPdS1_d
_Z18dubins_path_sampleP10DubinsPathdPd

在 C++ 中,由于函数可以重载,所以在编译阶段,函数会被编译为 返回值+函数名+参数列表 的唯一符号。其中参数列表称为函数特征标。函数名+函数特征标构成 C++ 函数重载的关键。
不难推测,该编译器将返回值 int 标识为 _Z18,把返回值 double 标识为 _Z20;P10 用来隔离函数名和参数列表。

题外话

为什么 C++ 函数重载不考虑返回值?

由于 C/C++ 的函数返回值并非必须使用的。如果函数重载也考虑了返回值,那么在只有返回值不同的重载函数使用中,编译器会无法确定究竟要使用的是哪个函数。

e.g. 演示了 C++ 函数重载考虑返回值的问题。

double add (int, int); // 函数原型 0
int add (int, int); // 函数原型 1

int main() {
  int a = 10, b =20;
  add(a, b); // 这里使用的是哪个函数?
}
  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值