我知道静态变量在程序生命周期内仅初始化一次(参见here以供参考)。此外,静态变量在函数调用之间保持其值。那么,如何修改静态变量?
例如,如何在以下代码中修改“a”:
#include <stdbool.h>
void foo(){
static int a;
printf("%d\n",a);
a++;
printf("%d\n",a);
}
int main()
{
foo();
foo();
return 0;
}
相反,如何修改非静态变量?更具体地说,如何在下面的代码中修改“a”?
#include <stdbool.h>
void foo(){
int a = 2;
printf("%d\n",a);
a++;
printf("%d\n",a);
}
int main()
{
foo();
foo();
return 0;
}
答案 0 :(得分:5)
在您的第一个示例中,静态变量具有本地范围,但与全局变量具有相同的生命周期,并在程序启动时初始化一次。当你想要产生副作用时,你会这样做 (某些东西被初始化,全局函数调用计数器......:即使它没有返回任何内容,调用子例程也有效...)
每次调用都有相同的地址(这意味着你可以返回它的地址并从某处修改它。)
在您的第二个示例中,您定义了一个自动变量,每次都分配和初始化。它可能有一个不同的地址,具体取决于调用链(在所有调用中,递归,线程,变量保证是唯一的,并且不能被其他调用/线程修改:没有边缘效应,重新启用)
在main()
上没有太大的区别,但是如果你把它放在一个子程序中,在第一种情况下,连续两次调用会产生不同的结果(2,3,然后是3,4),而在第二种情况下,2次连续调用将产生相同的结果(2,3次两次)
答案 1 :(得分:4)
另一个例子可能会使这一点更清晰。
假设您具有以下功能:
void foo( void )
{
int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}
我们创建一个变量a
,其初始值为1
,打印出其地址和值,然后递增它。在foo
函数的范围之外,此变量不可见。
变量a
的存储时间为auto
;其生存期仅限于foo
函数的生命周期。每次输入a
时,都会创建并初始化foo
的新实例,并且每次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
变量的空间取自运行时堆栈,尽管这是一个实现细节,而不是语言要求。
答案 2 :(得分:2)
如何更新静态变量?
我怀疑你对包含初始化程序的变量声明在语法上类似于赋值表达式这一事实感到困惑。具有静态持续时间(或任何其他变量)的变量最多初始化一次的事实与稍后如何修改该变量无关。它是关于程序语义的声明,而不是对代码的限制。
或者,您可能对“初始化”和“分配”之间的区别感到困惑。在这种情况下,这些具有特定的,不同的含义。这包含变量a
的初始化,但没有赋值给它:
int main() {
int a = 2; /* initialization */
printf("%d",a);
}
这包含对变量a
的赋值,但没有初始化它:
int main() {
int a; /* no initialization */
a = 2; /* assignment */
printf("%d",a);
}
这包含a
的初始化及其赋值:
int main() {
int a = 2;
printf("%d",a);
a = 3;
printf("%d",a);
}
您可以通过任何适合其类型的运算符更新静态变量,该运算符具有修改其操作数的副作用(=
,+=
,++
,等。),或通过指针以任意数量的间接方式。对于大多数用途,使用变量的代码不需要关注其存储持续时间。特别是,如果变量a
是静态的,上面的两个例子同样有效并产生相同的输出。不要认为静态变量没有特征用途,但“静态变量对什么有用?”是一个完全独立的问题。
答案 3 :(得分:0)
这个问题旨在说明如何修改静态变量,因为静态变量在函数调用之间保持其值。我也想知道上面的内容与修改任何非静态变量有什么不同。
根据发布的答案,事实证明所有变量都是直接(通过操作数)或间接(通过指针)修改。
但是,发布的答案强调了静态和非静态变量的存储持续时间的差异。
首先,重要的是要理解初始化,声明和分配之间的区别,正如{非常正式地指出} { {3}}答案指出,对任何类型的变量执行的斜体操作都不会影响它的存储持续时间。
接下来,重要的是要了解静态变量与全局变量具有相同的生命周期(无论静态变量是具有本地范围还是全局范围)。 John Bollinger's answer.指出了这一点。另外,有人可能会排除Jean-Francois Fabre's answer的播放,因为linkage的注释中指出,局部变量没有链接,而静态变量可能有内部,外部或无链接。但是,函数中的静态变量没有链接。
最后,必须认识到静态变量的行为方式,正如@Olaf
所指出的那样出色