C++虽然兼容C,但C++文件中函数编译后生成的符号与C语言生成的不同。因为C++支持函数重载,C++函数编译后生成的符号带有函数参数类型的信息,而C则没有。
例如 int add(int a, int b)
函数经过C++编译器生成.o文件后,add会变成形如 add_int_int
之类的, 而C的话则会是形如 _add
, 就是说:相同的函数,在C和C++中,编译后生成的符号不同。
这就导致一个问题:如果C++中使用C语言实现的函数,在编译链接的时候,会出错,提示找不到对应的符号。此时extern "C"就起作用了:告诉链接器去寻找_add这类的C语言符号,而不是经过C++修饰的符号。
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void *, int, size_t);
#ifdef __cplusplus
}
#endif
在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明
,在.c文件中包含了extern "C"时会出现编译语法错误。所以使用extern "C"全部都放在于cpp程序相关文件或其头文件中。不过与C++调用C接口不同,C++确实是能够调用编译好的C函数,而这里C调用C++,不过是把C++代码当成C代码编译后调用而已。也就是说,C并不能直接调用C++库函数。
//xx.h
extern int add(...)
//xx.c
int add(){
}
//xx.cpp
extern "C" {
#include "xx.h"
}
//xx.h
extern "C"{
int add();
}
//xx.cpp
int add(){
}
//xx.c
extern int add();
当 extern 不与"C"在一起修饰变量或函数时,如在头文件中:extern int g_Int;
它的作用就是声明函数或全局变量的作用范围的关键字
,其声明的函数和变量可以在本模块或其他模块中使用,记住它是一个 声明不是定义
也就是说编译单元B要是引用编译单元A中定义的全局变量或函数时
,它只要包含A的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在链接时从模块A生成的目标代码中找到此函数。
注意:在使用extern时候要严格对应声明时的格式
,比如在一个源文件里定义了一个数组:char a[6];,在另外一个文件里用下列语句进行了声明:extern char *a;这样是不可以的。但这同时也就造成另一个问题:如果函数原型被单方面修改的话,调用方在不知情情况下进行调用,在编译阶段不会报错
,但是在运行时会由于参数类型不匹配而出错,所以现在大多只在头文件中进行声明并使用#include头文件的方式来引入函数而非extern。
extern的常用用法:在源文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在头文件中并用extern来声明。
作用域扩展用法:
//"externTest.h"
#pragma once
#include
int _g_o = 100;
void _g_fun() {
std::cout << "Hello, World!" << std::endl;
_g_o = 999;
}
// main.cpp
#include
//不引入头文件会编译报错。没有引入情况下, extern int _g_o = 1; 这样是可以的 取值为1
//引入头文件情况下,不需要给_g_o 赋值,能共享 externTest 文件中定义的值
#include "externTest.h"
extern int _g_o;
extern void _g_fun();
int main() {
std::cout << "_g_o: "<< _g_o << std::endl;//输出100
_g_fun(); //能正常调用外部文件的方法
std::cout << "_g_o: "<< _g_o << std::endl;//输出999
return 0;
}