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); // 这里使用的是哪个函数?
}

相关推荐

  1. C/C++混合项目程序运行定义函数符号

    2024-04-04 06:10:03       36 阅读
  2. uniapp 小程序运行plus...

    2024-04-04 06:10:03       30 阅读
  3. 解决nodejs内存泄漏问题,项目无法运行

    2024-04-04 06:10:03       55 阅读
  4. sqoop运行

    2024-04-04 06:10:03       60 阅读
  5. spark运行

    2024-04-04 06:10:03       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-04 06:10:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-04 06:10:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-04 06:10:03       82 阅读
  4. Python语言-面向对象

    2024-04-04 06:10:03       91 阅读

热门阅读

  1. HTML优化SEO的实用技巧

    2024-04-04 06:10:03       35 阅读
  2. 大话设计模式之组合模式

    2024-04-04 06:10:03       29 阅读
  3. 我的创作纪念日

    2024-04-04 06:10:03       45 阅读
  4. acrn guest 内存分析

    2024-04-04 06:10:03       29 阅读
  5. DockerFile启动jar程序

    2024-04-04 06:10:03       31 阅读
  6. Sass学习记录

    2024-04-04 06:10:03       32 阅读
  7. NRM详解

    2024-04-04 06:10:03       38 阅读
  8. SpringBoot如何集成nacos,用于服务发现和配置管理

    2024-04-04 06:10:03       35 阅读
  9. 【Next.js】连接 MongoDB 实现基本的接口

    2024-04-04 06:10:03       36 阅读
  10. MongoDB聚合运算符:$lte

    2024-04-04 06:10:03       33 阅读
  11. 金融出海机遇与挑战

    2024-04-04 06:10:03       29 阅读
  12. Linux初学(十二)AWK进阶

    2024-04-04 06:10:03       26 阅读