extern "C"指示编译器这段代码按照c的方式编译,猛然看到这句话会感觉到很懵逼,下面我们来看看extern "C"出现的背景
对比下C++ C ObjectC三种语言发现
C++支持重载
比如可以这样
void testA() {
printf("%d", a);
}
void testA(int a) {
printf("%d", a);
}
C不支持支持重载
void testA() {
printf("%d", a);
}
void testA(int a) {
printf("%d", a);
}
编译会出现redefinition of 'test'
OC
@implementation ViewController
- (void)test:(int)a{
} - (void)test:(NSString*)a{
}
@end
编译会出现Duplicate declaration of method 'test:'
注意在OC中方法名如果带参数的话,会加上":"
如果是下面两个方法, - (void)test:(int)a
- (void)test
没问题,一个是test: 一个是test
所以在C++中,编译器会将方法void test()和void test(int a);编译成这样的符号
(使用nm -m 目标文件路径)
0000000000000030 (__TEXT,__text) external __Z5testAi
0000000000000000 (__TEXT,__text) external __Z5testAv
这个i和v表示参数是int和void类型,返回值没有体现出来,这个说明两个方法如果返回值不一样,其他都一样的话,也只是一个方法,不能共存的
在看看C中的方法void test(int a);编译之后成什么样子
0000000000000030 (__TEXT,__text) external _testA
仅仅是个testA,参数和返回值没有体现,这侧面跟C的不能重载特性相匹配
在看看OC中的方法 (void)test:(int)a编译之后成什么样子
0000000000000210 (__TEXT,__text) non-external -[ViewController test:]
同C语言
那如果C++直接调用C中的方法会出现什么问题呢
比如
ModuleA是用C写的,代码如下
TestModuleA.h
ifndef TESTC___TESTMODULEA_H
define TESTC___TESTMODULEA_H
int testFun(int, int);//声明
endif //TESTC___TESTMODULEA_H
TestModuleA.c
int testFun(int a, int b){//定义
return a+b;
}
main是用C++写的,代码如下
main.cpp
include <iostream>
include "TestModuleA.h"
using namespace std;
int main() {
cout << testFun(2, 3) << endl;
}
那么运行时(链接阶段,寻找符号)会出现以下错误
[ 50%] Linking CXX executable TestC__
Undefined symbols for architecture x86_64:
"testFun(int, int)", referenced from:
_main in main.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
其实就是找不到testFun(int, int)这个符号,为什么呢
那我们首先看看main的符号表,
(undefined) external __Z7testFunii
undefined表示不在main定义,但是在main中使用的方法,它的符号是__Z7testFunii,其实就是跟 "testFun(int, int)"对应,ii表示int,int
再看看TestModuleA的符号表
TestModuleA
nm -m TestModuleA.o
0000000000000000 (__TEXT,__text) external _testFun
testFun的方法对应的符号是_testFun,
这样看来就很明显,需要的跟实际的符号不匹配,所以找不到符号
那么怎么解决呢?
extern "C"应运而生
再来回顾下它的含义
让被作用的代码块采用c语言的编译规则编译
修改下TestModuleA.h的代码如下
ifndef TESTC___TESTMODULEA_H
define TESTC___TESTMODULEA_H
ifdef __cplusplus //而这一部分就是告诉编译器,如果定义了__cplusplus(即如果是cpp文件,
extern "C"{ //因为cpp文件默认定义了该宏),则采用C语言方式进行编译
endif
int testFun(int, int);
ifdef __cplusplus
}
endif
翻译一下当一个Cpp文件引入TestModuleA.h,它就变成
ifndef TESTC___TESTMODULEA_H
define TESTC___TESTMODULEA_H
extern "C"{
int testFun(int, int);
}
endif
这样的话,当cpp文件编译的时候发现extern "C"的时候,它就会按照C的方式寻找符号,即寻找_testFun这个符号,不信,
你再看看重新编译之后的main.o的符号表
(undefined) external _testFun
同理如果在C++里面有这样一段代码
extern "C"{
void testfunAAAa(int a);
}
void testfunAAAa(int a){
}
那么它的符号表是这样的
0000000000000000 (__TEXT,__text) external _testfunAAAa
这样的话,C就可以调用C++的方法了,
同理如果OC想调用C++代码的话,可以使用extern "C"
也可以将OC的文件后缀改为mm,mm兼容c++的编译方式,查找符号时
按照C++的方式查找,但是mm里面如果有调用C方法时,那就会找不到C里面的符号,因为它是按照c++的方式查找到,这时也可以使用extern "C"解决问题
个人理解,extern "C"有两层意思,
1.寻找方法符号的时候,按照C的规则寻找
2.编译方法的时候,按照C的规则编译,生成C的命名规范






网友评论