背景描述

日常的项目开发构建中, 常常出现 未定义的引用的错误。 出现这种错误的原因有很多, 此文描述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++链接错误:未定义的引用“引发的思考相关推荐

  1. linux链接时报未定义的引用,g ++链接或引用不与本地安装的库一起使用:未定义的引用...

    我正在尝试在Ubuntu上编译一个quickfix程序,但我得到了对FIX::的未定义引用,好像-lquickfix选项没有放在g ++命令中.实际上,如果没有此链接选项,我会得到相同的结果. 首先, ...

  2. 什么是未定义的引用/未解决的外部符号错误,如何解决?

    本文翻译自:What is an undefined reference/unresolved external symbol error and how do I fix it? What are ...

  3. VS远程开发(远程调试)编译报错:对‘xxx’未定义的引用(设置库依赖顺序)(已解决)pthread(项目-->属性-->链接器-->输入-->库依赖项)

    如图在VS中对linux进行远程开发时,编译报错: 貌似是因为在代码中使用了pthread.h的函数,链接库依赖顺序出了问题,我在ubuntu里手动使用gcc main.c -lpthread -o ...

  4. 错误 对‘pcl::console::print(pcl::console::VERBOSITY_LEVEL, char const*, ...)’未定义的引用

    错误描述 CMakeFiles/robotChassis.dir/src/Motion.cpp.o:在函数'void pcl::detail::FieldMapper<pcl::PointXYZ ...

  5. 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 ...

  6. 使用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 解 ...

  7. 对main 未定义的引用_错误:ID返回1个退出状态(对“ main”的未定义引用)

    对main 未定义的引用 As we know that, 我们知道 Each program must have a main() function, compiler starts executi ...

  8. OpenCV4编译链接问题xxxx未定义的引用

    大多数情况是因为Anaconda中的部分库冲突,例如usr/lib/x86_64-linux-gnu/libSM.so.6:对'uuid_unparse_lower@UUID_1.0'未定义的引用 首 ...

  9. ffmpeg 编程程序 未定义的引用错误问题记录

    记录:今天在编译ffmpeg时发现报下面的错误,根据提示说没有定义,但事实上,我已经包含了库了,后面发现是因为包含库的顺序不对,感觉是一个大坑啊!!! 正确的顺序: STATIC_OLIB+= \ $ ...

最新文章

  1. 使用Tensor Expression张量表达式处理算子
  2. java循环购物车结算系统_原生JS实现购物车结算功能代码
  3. MYSQL监控-自带工具Query Profiler的使用
  4. phpmyadmin #1045 - Access denied for user 'root'@'localhost' (using password: NO)
  5. java选择安装路径的功能怎么实现_水槽怎么选择,从安装方式,材质功能,江水平给你一次性说清楚...
  6. linux下串口程序测试
  7. oracle rac 创建ocr,Oracle rac 11g在线添加ocr,votedisk
  8. 21幅非常有创意的倒影摄影作品欣赏
  9. 如何使用notepad++查看二进制bin文件
  10. 三级数据库笔记(完整)
  11. python实现拼多多自动回复_拼多多客服多开工具怎么配置多店铺客服?
  12. 梳理常见硬盘存储 I/O 接口相关简称
  13. 蓝桥杯python青少年_让孩子参加蓝桥杯大赛好吗
  14. 学习一下什么是SRE和DevOps
  15. zheng项目新建一个module学习学习
  16. 休谟问题和金岳霖的回答
  17. sqlserver笔记
  18. 高级开发工程师如何快速晋升为架构师?高级开发工程师与架构师到底有啥区别?
  19. 以色列顶级货运公司Zim向所有客户开放区块链平台
  20. java.lang.ClassNotFoundException:teat1问题和CentOS 8 jdk安装

热门文章

  1. UDP Socket接收缓冲区与netstat Recv-Q
  2. Android Design in Action — 以微信为例
  3. oracle及shell相关学习记录
  4. pacemaker+corosync+pcs实验
  5. Tomcat源码解析:启动
  6. 【微信小程序】个人信息页面/我的页面
  7. 关于LTE终端的所谓的五模、七模、十频、十一频
  8. [Android]listview图文混排
  9. PHP 创建与解析 XML
  10. 手把手教你开发photoshop面板插件(附demo和工具)