Bootstrap

一文彻底详解C/C++中 static 关键字

0. 前言

在C语言中,有一个关键字 static ,它通常有一下三种用法:

1. 修饰局部变量

2. 修饰全局变量

3. 修饰函数

下面我们就展开说说:

1. 修饰局部变量

咋们直接贴上代码来说:

让我们来一起看看这段代码中 testFunction()函数 反汇编之后的代码?

我们可以看到, static int num = 10; 这条代码是没有汇编语句的!为什么呢?直接说结论:

static 修饰局部变量的初始化发生在程序加载到内存后、main函数执行前

无论 testFunction() 函数是否被调用,只要程序运行,该变量就会被初始化。

所以说,就算我们在main函数中调用10次 testFunciton() 函数,num 变量也只会在main函数执行前初始化一次!并且 这个被 static 修饰的局部变量的生命周期是随进程的!进程只要不死,这个变量永远都在(静态存储区中)。

拓展说一下静态存储区

静态存储区通过包括三个部分: .bss 段、 .data 段 、 .rodata 段 (地址从高到低)

.bss 段中,存放的是未初始化的全局变量(默认为0)或者静态变量 和 显式初始化为 0 的全局变量和静态变量

.data 段中 ,存放的是显式初始化不为 0 的全局变量和静态变量

.rodata 段中,存放的是字符串字面量 和 const 修饰的全局变量和静态变量,例如:

const int rodata = 1949;
const char *string = "我是大帅哥!";

这个段中的数据,如果要修改,会出现 Segmentation Fault 的段错误,具体原因是因为 页表 无法转换造成的,页表的事情以后再说~

2. 修饰全局变量

我们看下面这两段来自不同源文件的两段代码其中一个文件中定义了static修饰的全局变量:

 

如果编译执行, 我们可以看到下面这样的链接错误

结论:

        被 static 修饰的全局变量,只能在当前文件中使用,不能在其他源文件中声明使用!

3. 修饰函数

我们看下面这两段来自不同源文件的两段代码其中一个文件中定义了static修饰的函数:

 

 如果编译执行, 我们也会看到下面这样的链接错误

结论:

        被 static 修饰的函数,只能在当前文件中使用,不能在其他源文件中声明使用!

4. C/C++中 static 的不同

对于 static 修饰局部变量 来说,在C语言中,static 修饰的无论是局部变量还是全局变量都必须是静态的,只能在main函数执行前初始化完毕。但是在C++中被 static 修饰的局部变量 是运行非常量表达式 的。

int calculate() {
    return 42; // 可能是一个运行时计算的值
}

void func() {
    static int x = calculate(); // 初始化延迟到首次调用func()
}

C++的底层实现原理

编译器会为每个包含static局部变量的函数生成一个隐藏的标志变量​(通常是一个bool),用于跟踪该变量是否已被初始化?下面是历程?

第一次调用函数时,检查标志变量。

若未初始化,执行初始化逻辑并更新标志。

后续调用直接使用已初始化的值。

总结

本质上呢:被static 修饰的全局变量和函数,是改变了变量或者函数的链接属性,将原来的外部链接属性改变成了内部链接属性。这样链接器(Link Editor)在链接的时候,就找不到这些变量或者函数的地址了,从而在其他文件中就无法使用被 static 修饰的函数了。

;