【问题标题】:C: Array of automatic storage class?C:自动存储类的数组?
【发布时间】:2019-04-22 05:57:59
【问题描述】:

我正在阅读C 如何编程,我有一个关于数组的存储类 的问题。书中说:

数组和结构是“静态”实体,因为它们在整个程序执行过程中保持相同的大小(当然,它们可能是自动存储类,因此每个都被创建和销毁定义它们的块进入和退出的时间)

我不确定 blocks 是什么意思,我目前的理解是 function/for/while 是块。我尝试了以下方法:

...
for (size_t i=1; i<=2; i++) {
    printf("round %c:", i+'0');
    int a[10];
    show_array(a, 10); // this is a function that prints all elements of `a`.
    for (size_t j=0;j<10;j++) {
        a[j]=8;
    }
}

我得到了输出:

round 1:0,0,0,0,-1160480784,22023,-1160481168,22023,1594487680,32766,
round 2:8,8,8,8,8,8,8,8,8,8,

似乎int a[10] 是静态的,而不是自动存储类,我是否遗漏了什么? (四种存储类我都懂)

如果您需要此信息,我使用ideone.com 运行我的测试。

【问题讨论】:

  • 你认为“自动存储类”是什么意思?
  • 32766-1160481168 是“未初始化的值”吗?如果是,为什么8 不是?
  • 我们有很好的证据表明该数组没有静态存储持续时间——任何具有静态存储持续时间的东西都会在程序开始时初始化为 0,除非您将其初始化为其他值。由于您的数组包含像 -1160480784 这样的垃圾编号,因此它不能具有静态存储持续时间(除非您的程序在其他地方出现内存错误)。
  • 根据 C 2018 6.8.2 2,复合语句是一个块。复合语句是一对大括号{},其中包含声明和/或语句。
  • @user7813604:当一个块的执行结束时,每个与该块关联的具有自动存储持续时间的对象的生命周期结束。当块的另一次执行开始时,在其中创建的每个这样的对象都有一个新的生命周期。每次迭代都是不同的。

标签: c


【解决方案1】:

您的程序具有科学上称为undefined behaviour 的内容。在您的情况下,它是在初始化它们之前使用(打印)一堆 int 对象的结果。

由于行为未定义,程序输出可以是任何东西,包括理论上可能导致某人相信明显的废话的字符流(例如 a 具有静态存储持续时间)。

实际上,如果您忽略语言标准的高级世界并查看,就会发生这种情况:

  • 在第 1 轮 a 包含垃圾值,即之前存储在该内存位置的任何内容的残余。
  • 在第 2 轮时,a 仍然包含垃圾值,即之前存储在该内存位置的任何内容的残余。

唯一的区别是垃圾看起来很熟悉,因为它恰好是你的垃圾。你可以看到它,因为垃圾人还没有机会收集它。然而,它仍然是垃圾——垃圾人在某个时候将其删除。

【讨论】:

    【解决方案2】:

    数组a 是一个自动 变量,因为它是在块范围内定义的,本例中的块是for 循环的主体。您看到上次设置的值这一事实并不意味着该变量是静态的。

    如果没有显式初始化,自动变量未初始化。这基本上意味着它们将包含上次使用它们时发生在这些内存位置中的任何内容。

    在循环的第一次迭代中,您会在数组中看到看似随机的值。然后变量在循环结束时超出范围,并在下一次迭代中创建一个新实例,但在这种情况下,它恰好是在上次创建的同一内存位置创建的,没有其他任何事情发生写入该内存位置,因此您最终会看到相同的值。如果您进行不相关的代码更改或使用不同的优化设置进行编译,则无法保证您会看到相同的行为。

    【讨论】:

      【解决方案3】:

      你确实错过了一些东西。 a 确实有自动存储,并且打印的值是内存被重复使用的结果,不是存储是持久的。这些值将在调试模式下重置(可能不是所有开发环境,但有些会将成员设置为 0xCCCCCCCC 在每次迭代)。此外,好的编译器(大多数编译器,如果您启用所有警告)会告诉您这里使用的是未初始化的数据。

      如果你仍然不相信我,试试这个例子。它将显示a 中的内存值被b 中存储的值覆盖。数组aif 语句的相关代码块的末尾不再存在,并且所有内存都可供系统使用。在下一次迭代中,它可能会用于数组b,这就是为什么您会看到数组b 中打印的值8,即使它们被分配给a

      for (size_t i=1; i<=2; i++)
      {
          if( i&1 )
          {
              printf("round %c:", i+'0');
              int a[10];
              show_array(a, 10);
              for (size_t j=0;j<10;j++) a[j]=8;
          }
          else
          {
              printf("round %c:", i+'0');
              int b[10];
              show_array(b, 10);
              for (size_t j=0;j<10;j++) b[j]=888;
          }
      }
      

      为了最终确认内存正在被重用,您可以修改show_array 以打印传入的原始指针,而不仅仅是单个元素。您应该每次看到相同的地址。

      【讨论】:

      • 当数组a自动存储时,是指数组本身是自动的还是数组中的元素是自动的?
      • 数组与其中的元素具有相同的存储类(数组中的元素怎么会比数组长?)
      • @DietrichEpp 说了什么。我会在答案中添加注释
      • @DietrichEpp:哦,我明白了!
      • 谢谢先生,我相信你,但这只是我的提问方式。
      【解决方案4】:

      “自动存储类”是指定义变量的范围。这意味着分配给变量的内存将在退出创建变量的范围时被释放,而不是为您初始化内存。你仍然必须自己做。您在循环的第二次迭代中看到 8 的原因是因为在前一次迭代中释放的内存被重新分配给了数组。

      【讨论】:

        【解决方案5】:

        当具有自动存储持续时间的对象的生命周期结束时,这仅意味着内存被释放(以便它可以用于其他目的)。它不保证内存被擦除或用于其他目的。

        当同一对象的另一个实例的生命周期开始时,会再次为其分配内存。它可能碰巧与之前使用的内存相同。无法保证它已被删除或用于其他目的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-23
          • 2018-04-15
          • 2015-03-19
          • 1970-01-01
          • 2018-02-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多