C++调用C的库函数 undefined reference to

本文探讨了C++调用C库函数时遇到的链接错误问题。详细解释了C和C++编译器处理函数名的不同方式导致的链接失败现象,并介绍了如何通过extern C来解决此类问题。
C++调用C的库函数时,如果头文件定义得不恰当,可能会出现明明某函数在obj文件中存在,但是却发生链接失败的情况,出现如下错误:

undefined reference to 'xxx'

出现问题的原因是c库函数编译成obj文件时对函数符号的处理和C++不同。因为C++函数支持重载,所以函数符号的处理要更复杂一些,c往往不作修饰。

例如有函数:

/* dofunc.c */

#include <stdio.h>
intdofunc()
{
printf( "dofunc\n");
}

使用gcc编译成obj后
gcc -c dofunc.c
#生成 dofunc.o

objdump -x dofunc.o

[0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 dofunc.c
File
[2](sec1)(fl 0x00)(ty20)(scl 2) (nx 1) 0x00000000 _dofunc
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[4](sec1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text
AUX scnlen 0x14 nreloc 2 nlnno 0
[6](sec2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .data
AUX scnlen 0x0 nreloc 0 nlnno 0
[8](sec3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .bss
AUX scnlen 0x0 nreloc 0 nlnno 0
[ 10](sec4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rdata
AUX scnlen 0x8 nreloc 0 nlnno 0
[ 12](sec0)(fl 0x00)(ty20)(scl 2) (nx 0) 0x00000000 _printf



c的dofunc函数在obj文件里的符号为 _dofunc

再看看使用g++编译后的代码:

g++ -c dofunc.c

objdump -x dofunc.o

SYMBOL TABLE:
[0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 dofunc.c
File
[2](sec1)(fl 0x00)(ty20)(scl 2) (nx 1) 0x00000000 __Z6dofuncv
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[4](sec1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text
AUX scnlen 0x14 nreloc 2 nlnno 0
[6](sec2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .data
AUX scnlen 0x0 nreloc 0 nlnno 0
[8](sec3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .bss
AUX scnlen 0x0 nreloc 0 nlnno 0
[ 10](sec4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rdata
AUX scnlen 0x8 nreloc 0 nlnno 0
[ 12](sec0)(fl 0x00)(ty20)(scl 2) (nx 0) 0x00000000 _printf

g++编译后的函数符号名比较古怪: __Z6dofuncv

可见C和C++在加工函数名方面是很大不同的。

如果有C++程序要使用 dofunc.o , 如下程序的函数声明是错的

// main_dev.cpp

int
dofunc();

intmain( intargc , char* args[])
{
dofunc();
system( "pause");
}

g++ -o main_dev main_dev.cpp dofunc.o
main_dev.cpp: undefined reference to `dofunc()'
collect2: ld returned 1 exit status

原因是dofunc函数在加工后函数名应该为 __Z6dofuncv ,dofunc.o文件里面的是_dofunc,所以找不到。

如果有dofunc的源代码,解决办法很简单,将dofunc.c使用c++来编译即可。
如果不幸地dofunc函数在别人的库里面,而这个库是用c编写和gcc编译的,源代码不可见,那怎么办呢?
幸亏C++和编译器的设计者早已料到了这个问题,并提供了一种通用的解决办法:使用extern "C"来修饰旧C库的外部函数声明。

extern "C" {
int dofunc();
}

intmain( intargc , char* args[])
{
dofunc();
system( "pause");
}

g++ -o main_dev main_dev.cpp dofunc.o
成功

extern "C"修饰内的函数,一律按照c的风格来编译,以便能够链接到用c编译出来的obj库上去。

常见有形如:

#ifdef __cplusplus
extern "C" {
#endif

intdofunc();


#ifdef __cplusplus
}
#endif

的头文件声明。

这种的头文件一般是库开发者提供的,能同时被c和c++模块使用。宏 __cplusplus 是c++编译器定义的,这种写法保证了用C++编译时extern "C" 能生效;而用c编译时又不会因不会处理extern "C"而错误。

反过来,如果c需要调用C++编译的库又怎么办呢?相信一般情况下不会有这样奇特的要求,直接用C++编译不就完了?

把main_dev.cpp改名为main.c ,然后

gcc -o main_dev main_dev.c dofunc.o

当然会出现: undefined reference to `dofunc'

因为fofunc.o里面的符号是
__Z6dofuncv ,所以链接会失败,只能有一种非常恶心的方法去链到那个函数:

//main_dev.c

int(*dofunc)(); /* 声明函数指针 */

int _Z6dofuncv(); /* 会链接到 __Z6dofuncv */


intmain( intargc , char* args[])
{
dofunc=_Z6dofuncv; /* 函数指针赋值 */
dofunc();
system( "pause");
}

gcc -o main_dev main_dev.c dofunc.o
成功


上面讲了那么多,中心意思都是c和c++编译和链接时对函数名加工的细节问题,理解了这些细节后,如何运用完全就存乎一心了。

以上浅见,欢迎指正

本文出自 “软件工匠笔记” 博客,请务必保留此出处http://linhs.blog.51cto.com/370259/140927


### 编译时出现 'undefined reference to std' 错误的原因 当编译器报告类似于 `undefined reference to std::allocator::allocator()` 或者其他标准库函数的错误时,这通常意味着链接阶段出现问题。具体来说,这些错误表明编译过程中未能找到 C++ 标准库的相关实现[^1]。 对于 GCC 编译器,默认情况下它可能不会自动链接 C++ 标准库,因此需要显式指定 `-lstdc++` 参数来确保正确链接所需的库文件[^4]。 另外一种常见情况是在集成开发环境(IDE),比如 VSCode 中配置不当时也会引发此类问题。如果 IDE 的构建工具链设置不当,则可能导致即使命令行能够正常工作的情况下仍然收到类似的未定义引用错误[^2]。 ### 解决方案 为了修复这类编译期错误,建议采取如下措施: #### 使用正确的编译选项 确保使用支持 C++ 的编译器并加上适当的标准库链接标志。例如,对于简单的 Hello World 应用程序,应该这样调用 GCC 来编译 C++ 文件: ```bash gcc -g hello_world.cpp -lstdc++ -o hello_world.exe ``` 这里的关键在于加入了 `-lstdc++` 这一参数,用于指示链接器加载 C++ 标准模板库 (STL)。 #### 配置好 IDE 设置 如果是通过像 Visual Studio Code 这样的编辑器来进行项目管理的话,请确认其内部使用的任务 runner 和 launch.json 已经被适当地调整过以适应 C++ 开发需求。特别是要保证选择了合适的编译器路径以及传递给它的参数包含了必要的 C++ 支持。 #### 完整的例子 考虑一段非常基础的 C++ 程序: ```cpp #include <iostream> int main(){ std::cout << "Hello, world!" << std::endl; return 0; } ``` 尝试以上述方式对其进行编译,并执行生成的应用程序将会得到预期的结果而不是任何关于 “undefined reference”的警告或错误消息。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值