C 中的 static

印象中之前找过好几次关于 C 语言中 static 关键字的资料,但是一直忘,其实也是找到的资料都没说清楚,这里是一个总结和汇总。


0x01 static 全局变量

一个进程在内存中的数据布局如下(不带说明的是在这个问题中不重要):

内存中布局说明
栈区
...
堆栈增长区
...
堆区
其他段
.bas段未初始化的全局变量
.data段已初始化的全局变量
.text段进程二进制文件

其中 .data 段和 .text 段的生命周期和进程是同步的。

当一个进程的全局变量被声明为 static 之后,我们称静态全局变量。静态全局变量和其他的全局变量的存储地点并没有区别,都是在 .data 段(已初始化)或者 .bss 段(未初始化)内,但是它只在定义它的源文件内有效,其他源文件无法访问它。所以, static 可以限制全局变量的作用域在当前文件内。

现在举个例子:

== head.h

#include <stdio.h>

void print();

== head.c

#include "head.h"

static char* hello = "Hello World!";

void print()
{
    printf("%s\n", hello)
}

== main.c

#include "head.h"

int main()
{
    print();
    printf("%s\n", hello);
    
    return 0;
}

编译这个程序会报错:

main.c: In function ‘main’:
main.c:6:17: error: ‘hello’ undeclared (first use in this function)
printf("%s\n", hello);
                 ^
main.c:6:17: note: each undeclared identifier is reported only once for each function it appears in

在 main.c 中删除 printf("%sn", hello); 程序可以正常编译运行。在这个例子中,hello 就是一个静态全局变量

0x02 static 局部变量

局部变量在栈区上分配,这个局部变量所在的函数被多次调用时,每次调用这个局部变量在栈上的位置都不一定相同。局部变量也可以在栈上动态分配,但是记得使用完后释放。

静态局部变量和普通的局部变量相比起来有如下区别:

  1. 位置:静态局部变量在 .data 区,所以它虽然是局部变量,但是生命周期是整个程序的生命周期;
  2. 访问权限:静态局部变量只能被其作用域内的变量或函数访问。也就是说虽然它会在程序的整个生命周期中存在,由于它是static的,它不能被其他的函数和源文件访问;
  3. 值:静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。这个比较好理解,每次函数调用静态局部变量的时候都修改它然后离开,下次读的时候从全局存储区读出的静态局部变量就是上次修改后的值。

例子:

== head.h

#include <stdio.h>

void print();

== head.c

#include "head.h"

void print()
{
    int normal = 0;
    static int stat = 0;    // 静态局部变量
    printf("normal=%d --- stat=%d\n", normal, stat);
    normal ++;
    stat ++;
}

== main.c

#include "head.h"

int main()
{
    print();
    print();
    print();
    print();
    printf("call stat in main: %d\n", stat);
}

在 main 中调用 stat 会出错:

main.c: In function ‘main’:
main.c:9:39: error: ‘stat’ undeclared (first use in this function)
     printf("call stat in main: %d\n", stat);
                                       ^
main.c:9:39: note: each undeclared identifier is reported only once for each function it appears in

删掉 main.c 中最后一行的 printf,运行结果如下:

normal=0 --- stat=0
normal=0 --- stat=1
normal=0 --- stat=2
normal=0 --- stat=3

可见,函数每次被调用,普通局部变量都是重新分配,而静态局部变量保持上次调用的值不变。

需要注意的是由于static局部变量的这种特性,使得含静态局部变量的函数变得不可重入,即每次调用可能会产生不同的结果。这在多线程编程时可能会成为一种隐患。需要多加注意。

0x03 static 函数

C 语言中的 static 函数有些类似 C++ 中的 private

在 C 中,如果某些函数是为了对外提供的接口函数工作,其本身不希望被外界看到,这时可以用 static 修饰。

也即 static 修饰函数时,其作用是限制函数的作用域,同静态全局变量相似。

例子:

== head.h

#include <stdio.h>

static int called();
void print();

== head.c

#include "head.h"

static int called()
{
    return 6;
}

void print()
{
    int ret;
    ret = called();
    printf("ret=%d\n", ret);
}

== main.c

#include "head.h"

int main()
{
    int val;
    val = called();
    print();

    return 0;
}

编译会报错:

In file included from main.c:1:0:
head.h:3:12: warning: ‘called’ used but never defined
 static int called();
            ^
/tmp/ccP2zPe2.o: In function `main':
main.c:(.text+0xe): undefined reference to `called'
collect2: error: ld returned 1 exit status

删掉 main.c 中对 called() 的调用,运行结果如下:

ret=6

Tip:static 函数可以解决不同文件的函数同名问题

标签: Linux/C

发表评论: