C/C++中static和extern小结

时间:2022-08-10 15:43:27

static和extern是C/C++中和函数的声明有关的两个关键字,特别是涉及到全局变量时,所以做此总结。


1. static关键字

1.1 函数和变量声明(C/C++)

  1. static全局变量:

    当声明一个static全局变量,则表示静态全局变量,和其他变量一样,存放在.data(初始化了)或者.bss(未初始化)内,但只在定义它的源文件中有效,其余文件无法访问它

  2. static局部变量:

    具有以下特点:

    • 函数中声明一个static局部变量,不分配在堆或者栈上,也分配在.data(不分配在.bss)上

    • 虽然是局部变量,整个进程生命周期都存在,不被释放。

    • 虽然一直存在,其余函数不能访问,其余源文件更不能

    • 会被自动初始化(所以不在.bss)上(普通局部变量不会,普通全局变量会自动初始化)

    • 更进一步,每次调用这个变量时,其初始值都是上一次调用时修改的结果

  3. static函数:

    类似面向对象中private封装,其只能被所定义的源文件调用,不能再其他源文件中调用(即使包含头文件。。。。)

1.2 类成员的static(C++)

  1. 所有对象共有数据成员或者成员函数,类的静态成员,在类创建时就存在(不需要对象)。

  2. static关键字只能用于类内部的声明,不能用于类外部的定义。

  3. static成员函数没有this形参(因为不属于某个对象),可以直接访问static数据成员,不能直接使用非static成员。

  4. static成员函数不能声明为const,因为const成员函数真正的含义是不修改该成员函数所属的对象,而static成员函数不属于任何对象。

  5. 同上,static成员函数不能声明为虚函数!

  6. static不是对象的组成部分,只是类的组成部分

2. extern关键字

  1. extern关键字常用于头文件中变量的声明,表示这个变量的定义在其他文件中的全局变量,提示编译器当遇到这个变量时,在其他文件中查找。

  2. 现代编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的全局变量是互相不透明的。也就是说,在编译时,全局变量的可见域限制在文件内部。

    • 编译阶段是对各个文件进行单独编译,所以不会出错,链接阶段则出现了冲突,因为都是全局变量,变量名也一样
    • 需要注意的是,此时两个文件并没有使用include包含,只是一同编译。(如果使用include包含,则不需extern)
  3. 结合static来说,举例说明:

a.cpp如下:

int i = 1;

b.cpp如下:

#include <iostream>
using namespace std;

extern int i;
int main() {
int a = i + 1
cout << a << endl;
}

编译:

g++ -O0 -std=c++11 -o run ./a.cpp b.cpp
./run

结果:

2
  • 如果将b.cpp中extern去掉,则因为i在a.cpp中已经定义为了全局变量,当两个文件编译后进行链接时,会产生冲突,报错如下:
/tmp/ccednFyK.o:(.data+0x0): multiple definition of `i'
  • 如果将a.cpp中变量i加上static,同样出错,因为static限制了i只能在a.cpp中使用,报错如下:
b.cpp:(.text+0xa): undefined reference to `i'



值得注意的是:

C++将声明和定义分开就是为了防止重复编译导致大量不必要的开销,所以采用了“一处定义,多处声明”的策略,也就是头文件中只包含声明,将定义放在同名的代码源文件中。

Essential C++中强调了const object和inline函数是“一次定义”的例外

  • inline函数因为需要编译器对其进行展开,所以需要编译器在每个函数的调用点上都需要知悉函数的定义,所以常常将inline函数置于头文件中。

  • const object因为一出文件之外便不可见(const关键字的特性),所以需要头文件中进行定义(因为include头文件,所以可以调用)

    • 如果希望在其他文件中调用(非include包含)const全局变量,需要在变量的定义时显示声明为extern。这样在其他文件中,通过extern可以调用这个全局const object。(其实,非const的普通全局变量默认就是extern的)。
// a.cpp
extern const int i = 1;
int j = 2;
const int k = 3;

// b.cpp
extern const int i;
extern int j;
extern const in k;
int num = i + 1; // OK
int num = j + 1; // OK
int num = k + 1; // ERROR