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++的符号修饰


参考

extern使用方法总结

extern 用法,全局变量与头文件(重复定义)