一、前言
代码从编写到能执行之前,需要经过编译、链接阶段。通常,编译每一个单元文件会生成目标文件,然后链接器会把各个目标文件链接起来生成可执行性文件。链接器之所以能把目标文件相互之间链接起来,就是通过查找目标文件中的唯一函数符号(即经过编译器去编译修饰后,重新得到的函数符号)。
C和C++编译器对编译函数符号的生成规则是不一样的,例如对于函数void fun(int a, int b),C经过编译后生成的符号为_fun, C链接器链接的时候就会去找_fun这样的函数符号;在C++中,生成的类似为_fun_int_int, 新生成的符号名不仅带有函数名,还有参数类型。正因为他们两者编译函数的时候,生成的符号规则不一样,所以,在混合编程中,如果我们不进行任何处理,而相互效用的话,必然会出现在链接的时候,找不到符号链接的情况。
ps : c++符号名称的规则,并没有一种唯一的命名规范, 不同的编译器命名规范不同, 但是思路一致! 如下图所示 :
为什么编译器在设计的时候要这么设计生成的符号呢?
因为C只有单一的命名空间,不支持函数重载之类的特性
C++为了支持函数重载(即函数名字可以相同,参数类型或个数不同),允许存在同名的函数;且C++甚至可以存在相同的类型、变量等,因为在C++中命名空间的存在。
二、extern "C"
extern "C"的真实目的是实现类C和C++的混合编程。
在C++调用C的时候, 告诉C++编译器, 编译的时候,不要用默认的C++规范查找, 而要采用C规范去查找。
在C调用C++的时候, 告诉C++编译器, 编译的时候, 不要按照C++默认的规范去编译, 而应该按照C规范去编译。
三、c++引用c
1、简单示例
// 注意: 该代码是C程序, 请放在.c文件中, 这样确保是C编译器
#include <stdio.h>
void fun()
{
printf("ok\n");
}
// 注意: 该代码是C++程序, 请放在.cpp文件中, 这样确保是C++编译器
#include <iostream.h>
extern "C" void fun(); // 暂时骗过编译器, 并对链接器说, 你要按照C规范链接, 去找_fun, 而不是"?fun@@YAXXZ"
int main()
{
fun();
return 0;
}
2、代码优化:
既然#include相当于文本拷贝, 那为何不把extern "C" 语句放到头文件中呢?
但是我很遗憾的告诉大家, 如果你这样做的话, 那么C文件就不能调用C文件的方法了!因为C的编译器不支持 extern "C" 语法!这里要引出一个宏, __cplusplus, 只要是C++文件, 编译器就会自动定义一个这样的宏, 我们就能利用这个宏做到C/C++的终极混编了!
// --------------cfun.h
#ifndef __C_FUN_H__
#define __C_FUN_H__
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
void cfun();
#ifdef __cplusplus
}
#endif
#endif
// --------------cfun.c
#include "cfun.h"
#include <stdio.h>
void cfun()
{
printf("hello world.\n");
}
// --------------main.cpp
#include <iostream>
#include "cfun.h"
int main()
{
cfun();
system("pause");
return 0;
}
四、c引用c++
核心就是告诉C++编译器, 编译的时候, 不要按照C++默认的规范去编译, 而应该按照C规范去编译。其次,要实际谨记,include就是原地展开,这样就容易理解了!
方式1、函数在cpp文件中定义的时候使用extern "C"修饰
// cal.cpp
extern "C" int sum(int a, int b)
{
return a + b;
}
// hyber_test.c
#include <stdio.h>
extern int sum(int a, int b);
int main()
{
printf("sum:%d\n", sum(1,2));
}
方式2、在cpp头文件声明的时候使用extern "C"修饰
在cpp文件中用下列方式定义函数:
returnType FunName(parameters list).
然后在相应的头文件中进行声明:
extern "C" returnType FunName(parameters list);
在相应的.c文件中添加函数声明(extern returnType FunName(parameters list);),然后直接利用相应的函数即可.
cal.h:
#ifndef CAL_HEADER
#define CAL_HEADER
extern "C" int sum(int a, int b);
#endif CAL_HEADER
cal.cpp:
// cal.cpp
#include <cal.h>
int sum(int a, int b)
{
return a + b;
}
在C的代码文件main.c中调用print函数:
// hyber_test.c
#include <stdio.h>
extern int sum(int a, int b);
int main()
{
printf("sum:%d\n", sum(1,2));
}
note:
在C的代码文件中直接#include "cal.h"头文件,编译出错。
而且如果不加extern int sum(int a, int b);编译也会出错。
ref:
再谈谈只针对C++编译器/链接器的extern “C“------C与C++的相互调用_涛歌依旧的博客-CSDN博客
遇到问题 --- C++调用C静态库,出现undefined reference的问题 - 蜗牛的博客 | VK's Blog
C++程序中出现undefined reference to ......_阿卡基YUAN的博客-CSDN博客_undefinedreferenceto是什么意思
C和C++混合编译,extern和extern "C" - 云+社区 - 腾讯云
C/C++混合编程_embed_huang的博客-CSDN博客_c c++混合编程
浅谈C/C+混合编程 - 知乎
c/c++实现混合编程 - heity - 博客园
【重新认识C++】08-extern C2-C、C++混合开发_哔哩哔哩_bilibili