Shio Y. Blog

C语言中的static与extern

最近在学习 Operating System,作业需要使用 C 语言在一个 toy OS 中进行编程与学习。 在原始提供的源码中,发现有许多 staticextern 的关键字,故在此做一下总结与整理。

extern

首先来看一下 extern。其最主要的作用就是可以引用工程的其他文件中定义的变量或函数。 假设有两个文件 foo1.cfoo2.c

foo1.c


_10
int var;

foo2.c


_10
extern int var;

最终生成可执行文件时,需要分别编译 compile 和链接 link:


_10
gcc -c foo1.c
_10
gcc -c foo2.c
_10
gcc foo1.o foo2.o -o foo // link

在编译过程中,编译器只会为 foo2.c 中的 var 分配内存,在 foo1.c 中仅仅生成一个名为 var 的 symbol。而后在 link 过程中,链接器会将该 symbol 替换为 foo2.c 中的 var 的内存地址,从而达到指向同一变量的目的。

extern 的实际应用场景主要在编写库 library。打个比方,假设有名为 Mylib 的库,包含 Mylib.cMylib.h。 MyLib.c:


_10
int Variable;

MyLib.h:


_10
extern int Variable;

main.c:


_10
# include "MyLib.h"
_10
_10
int main ( void )
_10
{
_10
Variable = 10;
_10
printf ( "%d\n", Variable ) ;
_10
return 0;
_10
}

在。h 文件中使用 extern 来引用。c 库文件中定义的变量后,用户即可通过 include 头文件来使用库中的变量。

static

回到之前提到的例子,如果 foo2.c 中的 extern 去掉又会怎样呢。 对于未初始化的变量,GCC 会默认将它们视为同一个变量,效果与 extern GCC 会报错:


_10
/tmp/ccFN6SQZ.o: ( .data+0x0 ) : multiple definition of `var'
_10
/tmp/ccbc0T4O.o: ( .data+0x0 ) : first defined here

假设 foo1.cfoo2.c 分别来自于两个不同的库,如果用户想要同时 include 这两种库时,就会产生 multiple definition 的错误。作为库的开发者,有责任防止这种多重定义的情况的发生,因此需要用到 static。

static 保证了定义的变量和函数只存在该文件范围内,其他的文件无法 link 这些变量和函数,从而避免了多重定义的问题。

除此之外,定义在函数中 static 变量在函数被调用后可维持该变量的值。

来看一下下面的例子:


_18
void foo ( )
_18
{
_18
int a = 10;
_18
static int sa = 10;
_18
_18
a += 5;
_18
sa += 5;
_18
_18
printf ( "a = %d, sa = %d\n", a, sa ) ;
_18
}
_18
_18
int main ( )
_18
{
_18
int i;
_18
_18
for ( i = 0; i < 10; ++i )
_18
foo ( ) ;
_18
}

这种情况下,main 函数调用 foo 函数,在 foo 执行完退出后,sa 变量仍然保持其退出前的值。当 foo 被在此调用的时候,sa 会在原来的基础上再加 5

当我们需用保留调用函数的某种状态而又不想创建全局变量时,这种方法就显得比较有用。然而,这种方法的滥用会导致程序的可读性和线程安全,所以需要谨慎使用。

在 C++中,static 还可用于 class 属性,在此不再赘述。

参考:

  1. stackoverflow
  2. quora

写于 2016年03月26日