C 语言中,static是用于说明变量作用范围的关键字。它的作用有如下的三条

A. static

1. 隐藏

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为了理解全局可见性,我们举个例子:
假如我们要同时编译两个源文件,一个是a.c,一个是main.c

下面是a.c的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char a = 'A'; // global variable
void msg() {
printf("Hello\n");
}
```

下面是main.c的内容

``` c
int main(void) {
extern char a; // extern variable must be defined before
printf("%c ", a);
(void)msg();
return 0;
}

输出的结果:A Hello

因为未加static前缀的全局变量和函数都具有全局可见性,所以a.c中的全局变量a和函数msg都能在main.c中使用。如果变量和函数加static前缀,对其他文件就会隐藏。在a.c中,在a的定义前加上static,main.c就不能访问变量a了。利用statis可以避免命名冲突。

2. 不变量


static的第二个的作用是保持变量的持久。存储在静态数据区的变量会在程序刚开始运行时,就完成初始化。共有两种变量存储在***静态存储区***:全局变量和static全局变量。

全局变量(外部变量)的说明之前再冠以static 就构成了***静态的全局变量***。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别,在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

All static variables are global variable. Not all global variables are static.

1
2
3
4
5
6
7
8
9
10
11
int fun(void) {
static int count = 10; // 语句不会被执行过
return count--;
}
int count = 1;
int main(void) {
printf("global\t\tlocal static\n");
for(; count <= 10; ++count)
printf("%d\t\t%d\n", count, fun());
return 0;
}

在上述代码中,fun函数中static变量在程序执行前就已经初始化,值是10。函数fun在main中被调用了10次,并不会每调用fun一次,就初始化一次count。如果去掉count前的static,则输出都是10。

3. 默认初始化0


static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是“0x00”,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下。

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int a;
int main(void) {
int i;
static char str[10];
printf("integer: %d; string: (begin)%s(end)", a, str);
return 0;
}
输出:“ integer: 0; string: (begin)(end) ” (因为str为空)

B. register

register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。

register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。

首先,register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。

其次,因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。

由于寄存器的数量有限,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。

在某些情况下,把变量保存在寄存器中反而会降低程序的运行速度。因为被占用的寄存器不能再用于其它目的;或者变量被使用的次数不够多,不足以装入和存储变量所带来的额外开销。

早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定那些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。