由“c++链接错误:未定义的引用“引发的思考
背景描述
日常的项目开发构建中, 常常出现 未定义的引用的错误。 出现这种错误的原因有很多, 此文描述c++引用c函数导致的问题。
示例程序如下:
// add.c
int add(int a, int b)
{return a + b;
}
// test.c
int add(int , int);
int main()
{int a = 1;int b = 1;int c = add(a, b);return 0;
}
编译策略如下:
- gcc 编译add.c 和 test.c
- g++ 编译add.c 和 test.c
- gcc 编译add.c , g++编译test.c
gcc 编译add.c 和 test.c
编译过程如下:
[test_user]$ ls
add.c test.c
[test_user]$ gcc -c add.c test.c
[test_user]$ ls
add.c add.o test.c test.o
[test_user]$ gcc add.o test.o -o main_c
[test_user]$ ./main_c
[test_user]$
程序可以正确编译和运行。
g++ 编译add.c 和 test.c
[test_user]$ ls
add.c test.c
[test_user]$ g++ -c add.c test.c
[test_user]$ ls
add.c add.o test.c test.o
[test_user]$ g++ add.o test.o -o main_cpp
[test_user]$ ls
add.c add.o main_cpp test.c test.o
[test_user]$ ./main_cpp
[test_user]$
程序可以正确编译和运行。
gcc 编译add.c , g++编译test.c
[test_user]$ ls
add.c test.c
[test_user]$ gcc -c add.c -o add.o
[test_user]$ g++ -c test.c -o test.o
[test_user]$ ls
add.c add.o test.c test.o
[test_user]$ g++ add.o test.o -o main_cpp
test.o:在函数‘main’中:
test.c:(.text+0x21):对‘add(int, int)’未定义的引用
collect2: 错误:ld 返回 1
[test_user]$
连接时报错了。 原因时 test.c中没有找到add的定义, add不是在add.c中定义了么? 为什么会找不到呢 ?
其实 c编译后的符号与cpp编译后的符号是不同的, gcc编译 后的符号就是函数本身, 而g++编译后的符号是加了修饰的, 举例如下:
[test_user]$ gcc -c add.c -o add_c.o
[test_user]$ g++ -c add.c -o add_cpp.o
[test_user]$ nm add_c.o
0000000000000000 T add
[test_user]$ nm add_cpp.o
0000000000000000 T _Z3addii
[test_user]$
可以看到, gcc编译add.c后add.o中的符号就是add,而g++编译test.c后test.o中的符号是_Z3addii,所以链接时test.o找_Z3addii肯定是找不到的。
这种问题如何解决呢 ? 难道必须都使用gcc编译,或者都使用g++编译么 ? 肯定不是, 这就需要c++中的extern "C"上场了。
extern “C”
C++中使用extern “C” 声明或定义的内容以C语言的编译规则进行编译。
[test_user]$ cat test.c
int add(int , int);int main()
{int a = 1;int b = 1;int c = add(a, b);return 0;
}
[test_user]$ g++ -c test.c -o test.o
[test_user]$ nm test.o
0000000000000000 T mainU _Z3addii
[test_user]$ vim test.c
[test_user]$ cat test.c
extern "C" int add(int , int);int main()
{int a = 1;int b = 1;int c = add(a, b);return 0;
}
[test_user]$ g++ -c test.c -o test.o
[test_user]$ nm test.oU add
0000000000000000 T main
[test_user]$
可以看到, 加了extern "C"修改的add, 使用g++编译后符号位add; 而未加 extern "C"修改的add, 使用g++编译后符号为 _Z3addii。
加了 extern "C"修改的add, 也可以与gcc编译的add.c一起链接, 如下:
[test_user]$ g++ -c test.c -o test.o
[test_user]$ nm test.oU add
0000000000000000 T main
[test_user]$ gcc -c add.c -o add.o
[test_user]$ g++ add.o test.o -o test
[test_user]$ ./test
[test_user]$
extern "C"是C++才有的, C语言中并没有。 所以 test.c无法用gcc编译。此时就需要编译宏 __cplusplus 上场了。
编译宏 __cplusplus
__cplusplus 只在g++编译时被定义, gcc编译是未定义的, 所以可以通过此宏判断是g++编译还是gcc编译, 只有g++编译才能加extern “C”
[test_user]$ cat add.c
int add(int a, int b)
{return a + b;
}
[test_user]$ cat test.c
#ifdef __cplusplus
extern "C" int add(int , int);
#else
int add(int , int);
#endifint main()
{int a = 1;int b = 1;int c = add(a, b);return 0;
}
[test_user]$ gcc -c add.c -o add.o
[test_user]$ ls
add.c add.o test.c
[test_user]$ gcc add.o test.c
[test_user]$ ls
add.c add.o a.out test.c
[test_user]$ rm a.out
[test_user]$ ls
add.c add.o test.c
[test_user]$ g++ add.o test.c
[test_user]$ ls
add.c add.o a.out test.c
[test_user]$ ./a.out
[test_user]$
可以看到, gcc编译后的add.o, 既可以使用gcc与test.c链接, 也可以使用g++与test.c链接。
有的同学会问, 统一使用gcc或者g++编译不就好了, 为什么要做这种兼容?
原因是: 很多项目中静态库或动态库是C语言编写的, 而需要在C++程序中使用这些库函数, 此时就必须做兼容, 否则会出现“未定义的引用”错误。
由“c++链接错误:未定义的引用“引发的思考相关推荐
- linux链接时报未定义的引用,g ++链接或引用不与本地安装的库一起使用:未定义的引用...
我正在尝试在Ubuntu上编译一个quickfix程序,但我得到了对FIX::的未定义引用,好像-lquickfix选项没有放在g ++命令中.实际上,如果没有此链接选项,我会得到相同的结果. 首先, ...
- 什么是未定义的引用/未解决的外部符号错误,如何解决?
本文翻译自:What is an undefined reference/unresolved external symbol error and how do I fix it? What are ...
- VS远程开发(远程调试)编译报错:对‘xxx’未定义的引用(设置库依赖顺序)(已解决)pthread(项目-->属性-->链接器-->输入-->库依赖项)
如图在VS中对linux进行远程开发时,编译报错: 貌似是因为在代码中使用了pthread.h的函数,链接库依赖顺序出了问题,我在ubuntu里手动使用gcc main.c -lpthread -o ...
- 错误 对‘pcl::console::print(pcl::console::VERBOSITY_LEVEL, char const*, ...)’未定义的引用
错误描述 CMakeFiles/robotChassis.dir/src/Motion.cpp.o:在函数'void pcl::detail::FieldMapper<pcl::PointXYZ ...
- darknet出现以下错误 /home/ubtu/anaconda3/lib/libQt5Core.so.5:对‘ucnv_toUnicode_58’未定义的引用
darknet出现以下错误 /home/ubtu/anaconda3/lib/libQt5Core.so.5:对'ucnv_toUnicode_58'未定义的引用 collect2: error: l ...
- 使用gcc编译报错:/tmp/ccoLTk4o.o:在函数‘main’中: main1.c:(.text+0x9c):对‘pirnt1’未定义的引用 collect2: 错误:ld 返回 1
报错 使用gcc编译报错:/tmp/ccoLTk4o.o:在函数'main'中: main1.c:(.text+0x9c):对'pirnt1'未定义的引用 collect2: 错误:ld 返回 1 解 ...
- 对main 未定义的引用_错误:ID返回1个退出状态(对“ main”的未定义引用)
对main 未定义的引用 As we know that, 我们知道 Each program must have a main() function, compiler starts executi ...
- OpenCV4编译链接问题xxxx未定义的引用
大多数情况是因为Anaconda中的部分库冲突,例如usr/lib/x86_64-linux-gnu/libSM.so.6:对'uuid_unparse_lower@UUID_1.0'未定义的引用 首 ...
- ffmpeg 编程程序 未定义的引用错误问题记录
记录:今天在编译ffmpeg时发现报下面的错误,根据提示说没有定义,但事实上,我已经包含了库了,后面发现是因为包含库的顺序不对,感觉是一个大坑啊!!! 正确的顺序: STATIC_OLIB+= \ $ ...
最新文章
- 使用Tensor Expression张量表达式处理算子
- java循环购物车结算系统_原生JS实现购物车结算功能代码
- MYSQL监控-自带工具Query Profiler的使用
- phpmyadmin #1045 - Access denied for user 'root'@'localhost' (using password: NO)
- java选择安装路径的功能怎么实现_水槽怎么选择,从安装方式,材质功能,江水平给你一次性说清楚...
- linux下串口程序测试
- oracle rac 创建ocr,Oracle rac 11g在线添加ocr,votedisk
- 21幅非常有创意的倒影摄影作品欣赏
- 如何使用notepad++查看二进制bin文件
- 三级数据库笔记(完整)
- python实现拼多多自动回复_拼多多客服多开工具怎么配置多店铺客服?
- 梳理常见硬盘存储 I/O 接口相关简称
- 蓝桥杯python青少年_让孩子参加蓝桥杯大赛好吗
- 学习一下什么是SRE和DevOps
- zheng项目新建一个module学习学习
- 休谟问题和金岳霖的回答
- sqlserver笔记
- 高级开发工程师如何快速晋升为架构师?高级开发工程师与架构师到底有啥区别?
- 以色列顶级货运公司Zim向所有客户开放区块链平台
- java.lang.ClassNotFoundException:teat1问题和CentOS 8 jdk安装