C/C++ 混合编译 C++中的extern “C”

  • gcc编译*.c*.cpp文件依据各自的文件类型各自编译为C型目标文件和C++类型目标文件

  • g++编译*.c\*.cpp文件,都是编译为C++类型的目标文件

  • 使用g++时,不论是*.c还是*.cpp都将链接std c++库。而gcc是依据各自文件类型链接相应的std c库或者是std c++

当一个程序有通过g++编译的C++目标模块与通过gcc编译的C目标模块时,链接会报错。案例如下:

test.c

int sum(int a,int b){return a + b;
}

main.cpp

#include <iostream>
using namespace std;
int sum(int a,int b);
int main(){int a = 10;int b = 20;cout << sum(a,b) << endl;
}

终端中输入以下指令

xtark@xtark-vmpc:~/桌面/linux_study/section2$ gcc -c test.c
xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ -c main.cpp
xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ main.o test.o
main.o:在函数‘main’中:
main.cpp:(.text+0x21):对‘sum(int, int)’未定义的引用
collect2: error: ld returned 1 exit status

链接生成可执行文件的过程中报错显示找不到sum函数定义,这是为什么呢?通过汇编文件可以看出问题所在

在终端中输入以下指令:

xtark@xtark-vmpc:~/桌面/linux_study/section2$ gcc -S test.c
xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ -S main.cpp

test.s中部分内容

 .file   "test.c".text.globl   sum.type    sum, @function
sum:
.LFB0:.cfi_startprocpushq   %rbp

main.s中部分内容

 subq    $16, %rspmovl   $10, -8(%rbp)movl   $20, -4(%rbp)movl   -4(%rbp), %edxmovl  -8(%rbp), %eaxmovl  %edx, %esimovl  %eax, %edicall  _Z3sumii

可以看出,test.c通过gcc编译出的汇编文件中sum函数名是sum,而main.cpp通过g++编译出的汇编文件中sum函数名则变成了_Z3sumii。为什么会有这种差异呢?

这是因为C++支持多态性(这里是静态多态),也就是同名函数可以完成不同的功能,C++编译器通过函数参数区分具体调用的是哪一个函数。因此在编译的时候,C++编译器会将参数类型和函数名连接在一起,如main.s中的_Z3sumiiii表示两个参数都是int。于是在程序编译成为目标文件以后,链接器可以直接根据目标文件中的符号名将多个目标文件链接成一个可执行文件。

但是在C语言中,由于完全没有多态性的概念,C编译器在编译时基本对函数名什么都不会做,如test.s中的sum

在上面的例子中,main.s调用的是_Z3sumii函数,而test.c编译出的则是sum函数,那么链接器在链接时自然找不到函数定义而报错。

解决方法一:

为了解决这种问题,一种方法是将test.cmain.cpp都通过g++编译:

xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ test.c main.cpp -o main
xtark@xtark-vmpc:~/桌面/linux_study/section2$ ./main
30

可以看出正常调用了sum函数

解决方法二:

如果只提供了test.s汇编文件或者test.o目标文件,方法一就不可行。那么只能对main.cpp下手。可以通过extern "C"解决

main.cpp

#include <iostream>
using namespace std;
extern "C"{int sum(int a,int b);
}
int main(){int a = 10;int b = 20;cout << sum(a,b) << endl;
}

test.c不变

在终端中输入

xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ -S main.cpp
xtark@xtark-vmpc:~/桌面/linux_study/section2$ gcc -S test.c
xtark@xtark-vmpc:~/桌面/linux_study/section2$ g++ main.s test.s
xtark@xtark-vmpc:~/桌面/linux_study/section2$ ./a.out
30

可见成功调用了sum函数,可以通过汇编.s代码看出原因:

test.s没有变化

main.s中部分内容

 subq    $16, %rspmovl   $10, -8(%rbp)movl   $20, -4(%rbp)movl   -4(%rbp), %edxmovl  -8(%rbp), %eaxmovl  %edx, %esimovl  %eax, %edicall  sum

可以看出这里对于sum函数声明是按照C语言的方式编译的,因此不再是_Z3sumii而是sum,因此在链接时链接器自然能够找到test.o中的sum函数定义。百度百科定义:加上extern "C"后,会指示编译器这部分代码按C语言进行编译,而不是C++的。

一般在工程中,对于.c文件定义的sum函数,只需要在c文件的头文件中函数声明处添加extern "C"声明,然后在cpp文件中include该头文件即可。注意extern "C"只有C++编译器能通过编译,C编译器无法编译会报错。

test.h

#ifdef __cplusplus
extern "C" {#endif//...//各种函数声明int sum(int a,int b);//...
#ifdef __cplusplus
}
#endif

参考资料:
C和C++混合编译 - C++调用C
C和C++文件混合编译

C/C++ 混合编译 C++中的extern “C“相关推荐

  1. 如何混合编译C语言和C++

    如何混合编译C语言和C++ 实际开发过程中,C++中会调用C与语言编写的代码,我在网络上面找到一篇写得很好的文章 http://blog.csdn.net/keensword/article/deta ...

  2. c++中的 extern C

    c++中的 extern "C" 博客分类: c CC++C#GCCD语言  比如说你用C开发了一个DLL库,为了能够让C++语言也能够调用你的DLL输出(Export)的函数,你 ...

  3. VS2005混合编译ARM汇编代码

    2019独角兽企业重金招聘Python工程师标准>>> 在开发过程中,发现简单的在Storage Memory区域拷贝或粘贴文件不能达到硬件量测的要求,需要直接通过编写ARM汇编指令 ...

  4. C与C++编译的区别 C和C++混合编译

    原文链接,文章不错,修改了排版,看起来更加舒服. 关于extern_C 通常,在C语言的头文件中经常可以看到类似下面这种形式的代码: #ifdef __cplusplus extern "C ...

  5. VS里面关于.cpp文件与.cu文件混合编译问题---不要在.cpp文件声明核函数(__device__()和global__())

    原文出处:点击打开链接 ############################################################# 不要在.cpp文件声明核函数,否则会报错,具体可以参 ...

  6. C C++混合编译问题,gcc可以编译过,但是g++编译不过(restrict引起的问题)

    C C++混合编译问题 编译c c++混合的代码遇到的问题记录: 代码库中引入了string vetor等c++相关,链接的时候报错 如下: 对'std::allocator::allocator() ...

  7. c++与fortran混合语言编程中动态链接库的调用,[转载]C++与Fortran混合语言编程中动态连接库的调用...

    摘 要:介绍了C++和Fortran 90混合语言编程中,在C++中调用Fortran动态连接库(DLL,Dynamic Link Library)的方法以及参量传递.函数调用等的方法. 关键词:混合 ...

  8. 何时在C ++中使用extern

    本文翻译自:When to use extern in C++ I'm reading "Think in C++" and it just introduced the exte ...

  9. 华为鸿蒙OS发布!余承东:随时可替换安卓;方舟编译器将支持混合编译

    乾明 边策 郭一璞 发自 凹非寺  量子位 报道 | 公众号 QbitAI 千呼万唤始出来,华为鸿蒙操作系统,今日正式发布. 英文名HarmonyOS,寓意为和谐. 并不像亿万行代码的安卓那样笨重,鸿 ...

  10. makefile多目录的.c 格式.cpp混合编译

    原文地址:http://blog.csdn.net/yangbingzhou/article/details/50218315 [cpp] view plaincopyprint? # # c.cpp ...

最新文章

  1. python re库常用_Python re库指南
  2. oracle asm clsecho,ASM磁盘组一点管理
  3. Android下的Junit测试
  4. how to replace the note reuse project into my own one
  5. iOS应用如何支持IPV6
  6. mysql 5.5 主从同步问题_MySQL 5.5 主从复制异步、半同步以及注意事项详解
  7. 【C语言】将两个字符串连接起来
  8. html5的鼠标拖拽
  9. nparray和tensor的相互转化
  10. linux中哪一个标记可以作为子进程,linux系统编程试卷(答案)
  11. nginx开发_Filter模块执行顺序
  12. Zend Studio配置:使用PHP 7进行开发
  13. XPS文件怎么打开?可以转成PDF格式吗?
  14. Google商店应用上架注意事项
  15. 必须重启计算机才能关闭用户账户控制,Win10系统怎么彻底关闭用户帐户控制?...
  16. 【英语语法入门】 第29讲 情态动词的否定和疑问
  17. 【html】基础极速笔记
  18. 手把手教你用C写游程编码
  19. 医学统计学 第八章( t 检验)
  20. AutoSAR的入门知识

热门文章

  1. UICollectionViewCell复用时修改子页面属性出现混乱的解决方法
  2. 如何判断一个程序没有响应
  3. 猫哥教你写爬虫 029--爬虫初探-requests
  4. Zebras CodeForces - 950C
  5. 用A-priori算法实现frequent item set查找
  6. 【洛谷T2695 桶哥的问题——吃桶】
  7. day20遍历数组指定输出的数组
  8. 433 490 868 915Mhz lora频段贴片天线方案 CA-S01 CrossAir贴片天线
  9. 一次性计时器和间隔性计时器的实现
  10. 加州房价篇 (二) : 处理数据