extern "C"的原理
c++符号修饰和函数签名
gcc
-fleading-underscore
和-fno-leading-underscore
来开关在C语言符号前面加上下划线_
C++为了支持类,继承,虚机制,重载,命名空间等特性,发明了**符号修饰(name decoration)or符号改编(name mangling)**机制
函数签名:包含函数信息:函数名,参数类型,所在命名空间等
编译器会对其(函数签名)进行修饰,对应一个修饰后的符号
test.cc:
1 |
|
gcc -c test.c
编译成.o看看
肉眼可见默认以_Z开头,参数是int则i结尾…
可以通过c++filt
命令来解析被修饰的过程
c++filt _Z4funcf
-> func(flaot)
extern “C”关键字
c++为了在符号管理上和c兼容,通过extern "C"
关键字来声明或定义C符号
单独使用
大括号{}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// test.cc
#include <iostream>
extern "C" void func();
extern "C"
{
int var = 1;
void test() {}
}
int main()
{
func();
return 0;
}查看符号:
果然编译成C符号
在c++中使用c标准库函数时extern “C”的作用
例如使用void *memset(void *,int,size_t)
函数,在c++中编译后会修饰符号,在链接阶段和C库的memset符号链接肯定失败
且C语言不支持extern "C"
语言
兼容方法c++编译器通过__cplusplus
宏判断当前编译单元是否是C++代码,包含的<string.h>
内容:
1 |
|
所以系统头文件使用这种技巧~
extern “C” 例: C++程序调用C (普通情况)
> 不是用extern "C"的话 函数声明(未定义符号类型)是会加上签名的
> 用extern "C"则是声明和定义C的符号
code
1
2
3
4
5
6
7
8//test.cc
void func();
int main()
{
func();
return 0;
}编译:
g++ -c test.cc
1
2
3
4
5
6#include <me.h>
void func()
{
ps("C func");
}编译:
gcc -c func.c
可以nm看编译成.o的函数符号签名
和我们要用的c函数符号不相同
test.o
func.o
c++.o的函数符号和c.o的函数符号不相同
在test.cc中函数声明加上extern “C”关键字
查看符号,发现函数名没有加上签名
链接起来
g++ test.o func.o
./a.out
运行成功~
其他测试出来的方法:
例: C程序调用C++函数
c++函数:
1
2
3
4
5
6
7
8//Print.cc
#include <iostream>
int Print()
{
std::cout << "C++ func" << std::endl;
return 0;
}编译成.o
g++ -c Print.cc
nm查看c++函数的签名:
Print()的函数签名为:
_Z5Printv
.c文件中调用
1
2
3
4
5
6
7
8
9#include <me.h>
extern int _Z5Printv();
int main()
{
_Z5Printv();
return 0;
}编译:
gcc -c main.c
链接起来:
用到了c++的标准库,所以最后链接是通过g++来的
g++ main.o Print.o
./a.out
运行成功~
例:C++程序调用C封装的c++函数
编译:
g++ -c ManualNameMangling.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//ManualNamegling.cc
#include <me.h>
// 等待给c封装的c++函数
void func()
{
ps("c++ func\n");
}
// 调用封装好c++函数的c函数
extern "C" void all();
int main()
{
all();
return 0;
}查看
func()
的函数签名可以看到签名是
_Z4funcv
通过.c封装
1
2
3
4
5
6
7
8//Print.c
extern void _Z4funcv();//声明c++.o中的函数
//封装了c++函数
void all()
{
_Z4funcv();
}编译:
gcc -c Print.c
在.cc中
extern "C"
声明c函数,然后就能正常使用了最后链接起来:
g++ Print.o ManualNameMangling.o
./a.out
运行成功~
注1
ps宏是
#define ps(str) printf("%s",str)
自己修改一下就行了
nm -C
-C, --demangle[=STYLE] Decode low-level symbol names into user-level names
可以看做是把符号变为函数签名,默认是显示修饰过的符号
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!