【问题标题】:C initializes and re-initializes arrays to zero at runtime when asked当被询问时,C 在运行时将数组初始化并重新初始化为零
【发布时间】:2012-05-21 22:51:11
【问题描述】:
#include <stdio.h>

int main(void) {
    for (int i = 0; i < 5; i++) {
        int arr[5] = {0};
        // arr gets zeroed at runtime, and on every loop iteration!
        printf("%d %d %d %d %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]);
        // overwrite arr with non-zero crap!
        arr[0] = 3;
        arr[1] = 5;
        arr[2] = 2;
        arr[3] = 4;
        arr[4] = 1;
    }
    return 0;
}

显然这是可行的:

> gcc -Wall -Wextra -pedantic -std=c99 -o test test.c;./test
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

但是:

  • 幕后发生了什么?
  • 这是否保证适用于任何大小的数组?
  • 我是否找到了在循环的每次迭代中将数组归零的最优雅方法?

【问题讨论】:

  • 为了测试数组内容,循环并不是真正必要的尝试使数组成为全局变量(在发布中编译)并查看结果! ;)
  • 是的,这就是重点。使用 {0} 初始化数组,无需重置其值!
  • 我认为我们谈论的是不同的东西。而不是数组想象有:“int value = 0”。这是初始化,“value = 0”被重置。数组的 {0} 语法是 标准(仅当您想初始化为 0 时才有效),但您不能在声明之外使用它(因此循环本身对于测试/演示毫无用处目的)。读作:去掉循环,因为没用,问题是“{0} 做了什么?”
  • “作品”是偶然的;您在for 中未初始化i
  • 这将是一个“@pub”讨论!我的意思是,对于这个例子,循环是不相关的,因为你每次声明和初始化一个新变量(巧合的是,编译器可能会重用相同的堆栈空间)。这是一种可用于将数组初始化为零的语法(如链接帖子中所指出的)。如果将变量保留在循环之外并在循环中重置其内容,则循环可以演示某些内容,但不能使用该语法来完成。如果您将声明保留在那里,那么 loop scopedfunction scoped 只是演示初始化语法。无论如何...

标签: c arrays loops initialization runtime


【解决方案1】:

不要忽略代码的逻辑错误(在每次迭代时重新初始化)我建议在您想要分配数组的所有元素时使用 memcpy 或 memset。这是经过验证的方法,幸运的是,它可以作为几乎所有编译器实现的魅力。

Memset Example

MemCpy Example

所以,使用 Memset 你的代码会变成;;

int a[5]; 
memset(a,0,<number of bytes to be set to> sizeof(int)*5); 

请阅读文档以了解为什么它很快,您会喜欢...

编辑 对不起,感谢所有反对的选民/评论者......我更正了答案......

【讨论】:

  • 您在考虑哪些编译器?他们仍然支持吗? C99 标准至少要求它被支持。我对 C89 的要求有一些琐碎的怀疑;那里有一些限制,IIRC。
  • 上述代码不起作用的编译器严重损坏。 int a[5] = {0}; 由标准保证将所有数组元素初始化为 0。
  • -1 这不起作用。 memset 需要八位字节的大小,5 个整数大于 5 个八位字节。你断言这只适用于幸运的人也是错误的。它保证符合 C 语言标准。
  • int a[5] = {0} 在我的大学实习期间从未工作过......我们在 Borland C++ 编译器(Turbo C)上苦苦挣扎......我在 VC 上尝试过的任何方法,以及gcc,g++ 现在似乎可以工作了...所以我更正了我的答案...也将很快在 Tubro C++ 上进行测试,并让你们告诉结果...
  • 对 - Turbo C 是一个非常古老的编译器,并且仅限于(充其量)严格的 C89。但只有一个非常虐待狂的课程会让你在过去十年左右的任何时候使用它。但是,有趣的是,您似乎可以下载 Turbo C 2.01(大约从 1989 年开始),也可以下载 Turbo C++ 5.5(从相当近的时间开始)。请参阅 Wikipedia 和 Embarcadero(由 Google 提供)。
【解决方案2】:

是的,C 标准保证您的数组元素在每次循环迭代时都归零。如果您只希望它们在第一次迭代时归零,请将数组设为静态,即使用

static int arr[5] = {0}; 

在这种情况下,您可以省略= { 0 },因为需要初始化静态数据,就好像在没有初始化程序的情况下所有元素都被赋值为 0(或 NULL 用于指针,或 0.0 用于浮点值,递归对于结构)。

还请注意,如果您使用 less 初始化器比数组维度指定,其余元素分配零。所以

int arr[5] = {42, 0xdead};

等价于

int arr[5] = {42, 0xdead, 0, 0, 0};

而使用单个 0 只是此规则的一个特例。

【讨论】:

  • 严格来说,静态的意思是“如果你只想在程序启动时将它们归零”。他们不会在任何进入循环时归零;在第一次进入循环时,它们将为零。
  • @JonathanLeffler ...但是由于声明在循环的范围内,因此该变量确实不存在于范围之外,是吗?
  • @unwind:如果该变量在循环中被标记为static,则它具有从程序启动到退出存在(为其分配和初始化存储)的存储持续时间;除非从循环体内,否则它根本无法访问。如果像问题一样,它是一个自动变量,那么除了循环执行时,没有存储分配。
  • @JonathanLeffler:是的,我知道。我有点开玩笑,因为可以说,当一个变量无法以任何方式访问时,它是否被归零可以被认为有点没有实际意义。但是,当然,在范围提供的抽象下,您所描述的正是正在发生的事情。说得好。
【解决方案3】:

这个问题之前已经回答过:

幕后发生了什么?

编译器将生成与 memset 等效的代码(请参阅Strange assembly from array 0-initialization 了解完整详情)。

这是否保证适用于任何大小的数组?

不超过可用堆栈大小的限制,是的。

我是否找到了在循环的每次迭代中将数组归零的最优雅方法?

我想是的。 :)

【讨论】:

    【解决方案4】:

    变量arr 在循环的每次迭代中创建并初始化,因为那是您编写的。在您的示例中,代码执行类似于memset() 的操作以将数组设置为零,但可能更有效且内联。你也可以写一个非零初始化器。

    是的,它适用于“任何”大小的数组。如果数组更大,循环会更慢。

    这是一种非常优雅的实现效果的方式。编译器将尽可能快地完成它。如果你能写出一个更快的函数调用,那编译器作者就傻了。

    【讨论】:

      【解决方案5】:

      根据我的说法,每当您迭代循环时,您在循环内声明的任何变量都是该循环的本地变量。因此,在循环结束时它们会被破坏,当循环再次开始时,它们会再次被创建。 所以, 1. 以上回答; 2.是的,这应该适用于任何大小的数组; 3. 不...

      【讨论】:

      • 如果这不是在每次迭代中将数组归零的最优雅方式,您认为哪种方式更优雅?
      • 你还没有找到这个方法,这是唯一最简单的方法,可以做。我的“不”是你找到这个方法的问题。
      • 我想给你投票;你答案的其他部分很好。但是“否”违背了我对英语的理解,并阻止了我投票。
      【解决方案6】:

      您在每次迭代时都创建了一个数组,因此无需重新初始化。

      【讨论】:

      • 编译器不会告诉处理器...但是是的,它可能使用相同的区域。在这种情况下,只是因为每次创建arr时堆栈指针都有相同的地址。
      • 在 C 中,堆栈上的分配\解除分配只是由 arith 完成的。对堆栈指针esp 的操作。变得更快是非常困难的。而“说任何有价值的编译器”意味着这种行为取决于编译器,这是错误的(假设相同的操作系统)。
      猜你喜欢
      • 2020-12-18
      • 1970-01-01
      • 1970-01-01
      • 2018-07-26
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多