extern c
函数签名
Function Signature,函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。
编译过程中,编译器(或者链接器)处理符号时,他们使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称(Decorated Name)。
(内容主要摘抄《程序员的自我修养》)
以下表格是c++代码编译过后在gcc编译器下的符号名
函数签名 | 修饰后名称(符号名) |
int func(float) | _Z4funcf |
float func(float) | _Z4funcf |
int C::func(int) | _ZN1C4funcEi |
int C::C2::func(int) | _ZN1C2C24funcEi |
int N::func(int) | _ZN1N4funcEi |
int N::C::func(int) | _ZN1N1C4funcEi |
而gcc编译器编译纯c代码,修饰后名称跟原函数签名相同
extern
extern的问题在于不知道关键词出现的时候到底是声明还是定义。
声明可以多次,定义只能一次
要点:
1. 函数的声明extern关键词是可有可无的,因为函数本身不加修饰的话就是extern的。但是引用的时候一样是需要声明的 2. 而全局变量在外部使用声明时,extern关键词是必须的,如果变量无extern修饰且没有显式的初始化,同样成为变量的定义,因此此时必须加extern,而编译器在此标记存储空间在执行时加载如内存并初始化为0 3. 而局部变量的声明不能有extern的修饰,且局部变量在运行时才在堆栈部分分配内存分类:
(1)变量extern int a; // 声明一个全局变量a
int a; // 定义了一个全局变量
extern int a = 0; //定义个全局变量a,赋值初始化
int a = 0; //定义一个全局变量a,赋值初始化
(2)函数
// 定义了一个全局函数
int func(void)
{
return 0;
}
//声明了一个全局函数
int fun(void);
//函数声明,所以省略了extern,完整些是extern int fun(void);
对变量而言,如果你想在本源文件中使用另一个源文件的变量,就需要在使用前用extern声明该变量,或者在头文件中用extern声明该变量。
对函数而言,如果你想在本源文件中使用另一个源文件的函数,就需要在使用前用声明该变量,声明函数加不加extern都没关系,所以在头文件中函数可以不用加extern。
规则
规则1 头文件(.h)中是对于该模块接口的声明,接口包括该模块提供给其它模块调用的外部函数及外部全局变量,对这些变量和函数都需在.h中文件中冠以extern关键字声明;
规则2 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;
规则3 永远不要在.h文件中定义变量;
规则4 如果要用其它模块定义的变量和函数,直接包含其头文件即可。
实践
g++ -E main.cpp -o main.i -v
g++ -c main.cpp -o main.o -v
//查看
cat main.i
readelf -s mian.o
// 例子1
#include <stdlib.h>
int main() {
char *p = (char *)malloc(4);
free(p);
return 0;
}
可以在main.i看见 extern void *malloc (sizet _size)
是被包裹在extern c里面,也就是说这个malloc的函数签名按照gcc的符号修改修饰
再来看看main.o关于malloc的符号表,在符号表里面可见"malloc",所以程序能正常链接到标准c语言库的malloc
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.cpp
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000000 41 FUNC GLOBAL DEFAULT 1 main
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND malloc
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND free
// 例子2
#include <stdlib.h>
extern void *malloc(int);
int main() {
char *p = (char *)malloc(4);
free(p);
return 0;
}
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.cpp
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000000 41 FUNC GLOBAL DEFAULT 1 main
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z6malloci
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND free
按照g++的符号修饰