【问题标题】:Initialization and updation of variable types变量类型的初始化和更新
【发布时间】:2016-09-07 19:06:21
【问题描述】:

我了解静态变量在程序的生命周期内仅被初始化一次(参见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;
}

【问题讨论】:

  • 所有变量在其生命周期内只能被初始化一次(即当它们到达 lif 时)。不确定你的问题是什么。 “更新”是什么意思?它们只是被写入。
  • 这里显示的两个程序具有相同的输出。通过显示静态变量位于 main() 以外的函数中的示例可能有助于澄清您的问题。
  • main 以外的函数中更有意义,因为它可能会被多次调用。 static 局部范围变量只初始化一次,但这与在代码运行时分配另一个值完全不同。 static 限定符只是意味着在函数结束并且变量超出范围后,它会保留其值。

标签: c static


【解决方案1】:

在您的第一个示例中,静态变量具有局部范围,但与全局变量具有相同的生命周期,并在程序启动时初始化一次。当你想获得副作用时,你会这样做 (有些东西被初始化了,全局函数调用计数器……:调用子程序即使没有返回也有效果……)

它在每次调用时都有相同的地址(这意味着你可以返回它的地址并从某个地方修改它)。

在第二个示例中,您定义了一个自动变量,每次都分配和初始化。它可能具有不同的地址,具体取决于调用链(在所有调用中,递归、线程,变量保证是唯一的,并且不能被其他调用/线程修改:无边缘效应,可重入)

不会对main() 产生太大影响,但如果你把它放在一个子程序中,在第一种情况下,2 次连续调用将产生不同的结果(2,3,然后是 3,4),而在第二种情况,2 次连续调用将产生相同的结果(2,3 两次)

【讨论】:

  • C 标准不需要使用堆栈或堆。并且典型的实现不会在堆上分配统计变量,即使 iff 它们也支持动态内存分配。
  • C 标准没有“全局”链接。静态变量可以有内部、外部或没有链接。函数中的静态变量没有。无论如何,这不是联动的问题,而是存储时间的问题。传递局部变量的地址是常见的做法。实际上,问题中的代码显示了一种典型的用法。这没有什么奇怪的。
  • 该死的法语背叛了我。我认为这被称为副作用。意味着如果你调用子程序并忽略结果,程序的状态在子程序退出时是不同的。它可以是简单的打印到控制台、将文件写入磁盘或修改全局变量。
  • @simrandhamija:如果你想要它,请阅读标准:port70.net/~nsz/c/c11/n1570.html#6.2.2
  • @simrandhamija:请不要在这个网站上!它们是常用术语,与编程没有直接关系。
【解决方案2】:

一个不同的例子可能会更清楚一点。

假设你有以下函数:

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;
}

所有abc 都声明为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

abc 的新实例在进入它们各自的函数时创建和初始化,并在函数退出时销毁。它们根据函数在调用链中的位置获得不同的地址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 项目存储在一个非常不同的位置,但另一个平台可能不会显示出明显的差异。


  1. 实际上,auto 变量的空间取自运行时堆栈,尽管这是一个实现细节,而不是语言要求。
  2. 请注意,根据函数在调用链中的位置,相同的地址可能会如何重复用于不同的对象。

【讨论】:

    【解决方案3】:

    静态变量如何更新?

    我怀疑您对包含初始化程序的变量声明在语法上类似于赋值表达式这一事实感到困惑。具有静态持续时间的变量(或任何其他变量)最多初始化一次这一事实与您以后如何修改该变量没有任何关系。这是关于程序语义的声明,而不是对您的代码的限制。

    另外,您可能对“初始化”和“赋值”之间的区别感到困惑。在这种情况下,它们具有特定的不同含义。这包含变量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 设为静态,上述两个示例同样有效并产生相同的输出。不要认为静态变量没有特征用途,而是“静态变量有什么用?”是一个完全独立的问题。

    【讨论】:

    • 根据您的回答,可以在不初始化变量的情况下分配变量(在您的第二段代码中,您声明 a 并为其分配一个值。)那么什么构成初始化?
    • 还有,你说的副作用是什么意思?
    • @simrandhamija,初始化是赋予变量在其生命周期开始时的值的过程。从 C 语义的角度来看,变量一出现就具有初始值。特别是,在main() 开始执行时,所有具有静态持续时间的变量都有其初始值。
    • @simrandhamija:是的,您可以在不首先初始化变量的情况下分配变量。 static 变量(使用 static 关键字或在文件范围内声明)被隐式初始化为零,具体取决于其类型。没有初始化器的 auto 变量(在没有 static 关键字的函数或块中声明)具有 indeterminate 值,它可以包括 trap 表示(有点不对应于该类型的有效值的模式)。尝试读取未初始化的 auto 变量会导致未定义的行为。
    • @simrandhamija,C 中类型不是void 的每个表达式都会产生一个值。表达式还可能对执行环境产生其他影响,例如修改变量的值——除了计算值之外的所有这些影响都是“副作用”。例如,计算表达式 a = 2 会产生值 2,并具有将 a 的值设置为 2 的副作用。计算表达式 puts("Hello world!") 会产生一个值,该值指示函数在导致将字符串发送到标准输出的副作用
    【解决方案4】:

    考虑到静态变量在函数调用之间保持其值这一事实,该问题旨在说明如何修改静态变量。我也想知道以上内容与修改任何非静态变量有何不同。

    根据发布的答案,事实证明,所有变量都被直接(通过操作数)或间接(通过指针)修改.

    但是,发布的答案强调了静态变量和非静态变量的存储持续时间的差异。

    首先,重要的是要理解 InitializationDeclarationAssignment 之间的区别,正如 @ 所指出的(相当明确) 987654321@答案指出,对任何类型的变量执行的斜体操作都不会影响它的存储持续时间。

    接下来,重要的是要了解静态变量与全局变量具有相同的生命周期(无论静态变量具有局部作用域还是全局作用域)。 Jean-Francois Fabre's answer 指出了这一点。此外,有人可能会排除linkage 的作用,因为正如@Olaf 在cmets 中指出的那样,局部变量没有链接,而静态变量可能有内部、外部或没有链接。但是,函数内部的静态变量没有链接。

    最后,必须了解静态变量的行为方式,正如 John Bode's Answer 出色指出的那样

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 2016-11-03
      • 2020-08-05
      • 1970-01-01
      • 2012-02-19
      • 1970-01-01
      相关资源
      最近更新 更多