一个不同的例子可能会更清楚一点。
假设你有以下函数:
void foo( void )
{
int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}
我们创建一个变量a,初始值为1,打印出它的地址和值,然后递增。此变量在foo 函数范围之外不可见。
变量a具有auto的存储持续时间;它的 lifetime 仅限于 foo 函数的生命周期。每次输入foo 时都会创建并初始化a 的一个新实例,并且每次foo 退出1 时都会销毁该实例。
现在我们将 static 关键字添加到声明中:
void foo( void )
{
static int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}
a 现在有 static 存储期限; a 的单个 实例在程序启动时创建并初始化一次,并且该实例一直保持到程序退出。 a 仍然只能在 foo 的范围内按名称可见,但它的 生命周期 超出了 foo 函数的生命周期。
在这两种情况下,a 的值都以完全相同的方式更新;我们更新a对应的内存位置的内容。唯一的区别是该内存位置的内容如何在程序的生命周期内得到维护。
要开车回家,请输入以下代码:
#include <stdio.h>
void foo( void )
{
int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a );
a++;
}
void bar( void )
{
int b = 2;
foo();
printf( "&b = %p, b = %d\n", (void *) &b, b );
b++;
}
void bletch( void )
{
int c = 3;
bar();
printf( "&c = %p, c = %d\n", (void *) &c, c );
c++;
}
int main( void )
{
foo();
bar();
bletch();
bar();
foo();
return 0;
}
所有a、b 和c 都声明为auto(这是默认值)。当我构建并运行这段代码时,我得到以下输出:
&a = 0x7fff701d234c, a = 1
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d230c, a = 1
&b = 0x7fff701d232c, b = 2
&c = 0x7fff701d234c, c = 3
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d234c, a = 1
a、b 和 c 的新实例在进入它们各自的函数时创建和初始化,并在函数退出时销毁。它们根据函数在调用链中的位置获得不同的地址2。
如果我更改该代码以声明 a
static int a = 1;
我得到以下输出:
&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 3
&b = 0x7fffd1e75cac, b = 2
&c = 0x7fffd1e75ccc, c = 3
&a = 0x500a58, a = 4
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 5
因此,有几件事立即显而易见 - a 的地址不会随着每次调用 foo 而改变,并且 a 的值不会在每次调用时重新初始化。同样,a 的实例在程序启动时创建并初始化一次,并且该实例在 foo 的生命周期之后仍然存在。
将b 声明为static 给了我们
&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x500a5c, b = 2
&a = 0x500a58, a = 3
&b = 0x500a5c, b = 3
&c = 0x7fffc301f8cc, c = 3
&a = 0x500a58, a = 4
&b = 0x500a5c, b = 4
&a = 0x500a58, a = 5
不要过多地读取地址值本身;这一切都取决于平台。显然,在我的平台上,static 项目与auto 项目存储在一个非常不同的位置,但另一个平台可能不会显示出明显的差异。
- 实际上,
auto 变量的空间取自运行时堆栈,尽管这是一个实现细节,而不是语言要求。
- 请注意,根据函数在调用链中的位置,相同的地址可能会如何重复用于不同的对象。