static和extern
1. 声明与定义
声明:让变量类型与命名为编译器所知(变量);告知编译器函数签名(函数);
定义:用于为变量分配存储空间(变量);函数主体具体的实现(函数);
2. static
一般局部变量是存储在栈区的,局部变量的生命周期在其所在的语句块执行结束时便结束了。但如果用static修饰局部变量,那么这个变量就不会存储在栈区而是放在静态数据区,其生命周期会一直持续到整个程序结束,该变量只在初次运行时进行初始化,且只进行一次。
特点:
- 函数体内static变量的作用范围为该函数内,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
- 声明为static变量为全局(C++是namespace内全局)变量;
- 在命名空间内的static函数只可被该命名空间的其它函数调用;
- 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
- 在类中的static成员函数只能访问类的static成员变量,不能访问类中非static变量,不接受this指针。
如果是static修饰的全局变量,且实现的函数写在头文件(h)中,在其他文件也可以访问,如下:
3. extern
当在某个文件定义了一个全局变量,如果要在另一个文件去使用该变量,如果再次去定义,则会出现重复定义的问题,这个时候就需要使用到声明,对该变量的声明告诉编译器该变量在其他文件中已经定义,在此处要去引用它。
4. extern C
在解释extern C之前,先介绍函数签名;
4.1 函数签名(function signature)
- C函数签名:函数名
- C++的函数签名:包含了一个函数的信息,包括函数名、参数类型、参数个数、顺序以及它所在的类和命名空间。
由于C ++具有重载的函数名,而C没有重载,因此C ++编译器不能仅使用函数名作为要链接的唯一ID,因此它通过添加有关自变量的信息来破坏名称。假如某个函数的原型为void fun(int x, int y),该函数被C编译器编译后在符号库中的名字为_fun,而C++编译器则会产生namespace::_fun_int_int之类的名字。namespace::_fun_int_int这样的名字是包含了命名空间、函数名以及形参,C++就是靠这种机制来实现函数重载的。 不同的编译器厂商的名称修饰方法可能不同,所以不同的编译器对于同一个函数签名可能对应不同的修饰后名称。
4.2 extern C
extern “C”修饰的函数或者变量是按照C语言方式编译和链接的。以此来实现C和C++的混合编译。
(1) 在C++中引用C语言中的函数和变量,在包含C语言头文件时(假设为cExample.h),需进行以下处理:
/* c语言头文件:cExample.h */ #ifndef C_EXAMPLE_H #define C_EXAMPLE_H extern int add(int x, int y); #endif /* c语言的实现文件:cExample.c */ #include "cExample.h" int add(int x, int y) { return x + y; } /* c++实现文件,调用add:cppFile.cpp */ extern "C" { #include "cExample.h"; } int main() { add(2, 3); return 0; }
(2) C中引用C++语言中的函数或者变量时,C++的头文件需要加上extern “C”,但是C语言中不能直接引用声明了extern “C”的该头文件,应该仅在C中将C++中定义的extern “C”函数声明为extern类型。
/* c++头文件cppExample.h */ #ifndef CPP_EXAMPLE_H #define CPP_EXAMPLE_H extern "C" int add(int x, int y); #endif /* c实现文件cFile.c */ extern int add(int x, int y); int main() { add(2, 3); return 0; }
参考文献:
若我所见不同 独守一角青空。