在C / C ++中初始化数组是一种好习惯吗?

时间:2010-01-11 06:49:57

标签: c++ c arrays array-initialization

我最近遇到过一个案例,我需要比较两个文件(黄金和预期)以验证测试结果,即使写入两个文件的数据相同,文件也不匹配。

在进一步调查中,我发现有一个结构包含一些整数和一个64字节的char数组,并且在大多数情况下并没有使用char数组的所有字节,并且数组中未使用的字段包含随机数数据和导致不匹配的数据。

这让我想问一下,在C / C ++中初始化数组是否也是一个好习惯,就像在Java中完成一样?

8 个答案:

答案 0 :(得分:23)

在使用内存/变量之前初始化内存/变量是一种好习惯 - 未初始化的变量是通常很难追踪的错误的主要来源。

在将文件写入文件格式时初始化所有数据是一个非常好的主意:它使文件内容更清晰,因此更容易使用,如果有人错误地尝试“使用”未初始化的数据,则不易出现问题(记住它可能不仅仅是你自己的代码在将来读取数据),并使文件更具可压缩性。

在使用变量之前不进行初始化的唯一理由是在性能关键的情况下,初始化在技术上是“不必要的”并且会产生很大的开销。但在大多数情况下,初始化变量不会造成重大损害(特别是如果它们仅在使用之前立即声明),但通过消除常见的错误来源将为您节省大量的开发时间。

答案 1 :(得分:5)

在数组中使用未定义的值会导致未定义的行为。 因此,该程序可以自由地产生不同的结果。这可能意味着您的文件最终略有不同,或程序崩溃,或程序格式化您的硬盘驱动器,或程序导致恶魔飞出用户鼻子(http://catb.org/jargon/html/N/nasal-demons.html

这并不意味着您需要在创建阵列时定义数组值,但必须确保在使用之前初始化任何数组值。当然,确保这一点的最简单方法是在创建数组时执行此操作。

MyStruct array[10];
printf( "%f", array[2].v ); // POTENTIAL BANG!
array[3].v = 7.0;
...
printf( "%f", array[3].v ); // THIS IS OK.

不要忘记,对于庞大的POD阵列,有一个很好的速记将所有成员初始化为零

MyPODStruct bigArray[1000] = { 0 };

答案 2 :(得分:3)

我强烈不同意这样做的意见,即这样做是“消除常见的错误来源”或“不这样做会弄乱你的程序的正确性”。如果程序使用单元化值,那么它有一个错误并且不正确。初始化值并不能消除此错误,因为它们在第一次使用时通常仍然没有预期的值。但是,当它们包含随机垃圾时,程序更有可能在每次尝试时以随机方式崩溃。始终具有相同的值可能会在崩溃时提供更确定的行为,并使调试更容易。

对于您的特定问题,在将未使用的部分写入文件之前覆盖未使用的部分也是一种很好的安全措施,因为它们可能包含您不希望编写的先前使用的内容,例如密码。

答案 3 :(得分:1)

如果你没有初始化c ++数组中的值,那么值可以是任何值,所以如果你想要可预测的结果,最好将它们归零。

但是如果你像使用空终止字符串那样使用char数组,那么你应该能够将它写入具有正确函数的文件。

虽然在c ++中使用更多OOP解决方案可能更好。 I.E.矢量,字符串等

答案 4 :(得分:1)

请记住,保持阵列未初始化可能具有性能等优势。

从未初始化的数组中读取只是 。没有从未初始化的地方读取它们就好了。

此外,如果你的程序有错误使它从数组中的未初始化的位置读取,那么通过防御性地将所有数组初始化为已知值来“覆盖它”不是bug的解决方案,并且只能使它稍后浮出水面。

答案 5 :(得分:1)

可以写一篇关于人们可以遇到的两种风格之间差异的大文章,在声明变量时初始化变量的人以及在必要时初始化变量的人。我与第一类的人分享了一个大项目,而我现在更确切地说是第二类。 总是初始化变量带来了更多微妙的错误和问题,我将尝试解释原因,记住我发现的案例。 第一个例子:

struct NODE Pop(STACK * Stack)
{
  struct NODE node = EMPTY_STACK;

  if(Stack && Stack->stackPointer)
    node = Stack->node[--Stack->stackPointer];

  return node;
}

这是另一个人写的代码。这个函数是我们应用程序中最热门的函数(你想象一个三元树中500万个句子的文本索引,FIFO堆栈用于处理递归,因为我们不想使用递归函数调用)。 这是他编程风格的典型特征,因为他对变量进行了系统的初始化。该代码的问题是初始化的隐藏memcpy和结构的其他两个副本(有时候btw不是调用memcpy gcc的奇怪的东西),所以我们有3个副本+一个隐藏的函数打电话给项目最热门的功能。 将其重写为

struct NODE Pop(STACK * Stack)
{
  if(Stack && Stack->stackPointer)
    return Stack->node[--Stack->stackPointer];
  return EMPTY_STACK;
}

只有一个副本(以及它运行的SPARC上的补充好处,由于避免调用memcpy并且不需要构建新的寄存器窗口,该函数是一个叶函数)。所以功能快了4倍。

我发现的另一个问题是但是不记得究竟在哪里(所以没有代码示例,抱歉)。声明时初始化的变量,但它在循环中使用,switch处于有限状态自动机中。初始化值不是自动机状态之一的问题,在一些极少数情况下,自动机无法正常工作。通过删除初始化程序,编译器发出的警告明显表明该变量可以在正确初始化之前使用。修复自动机很容易。 道德:防御性地初始化变量可能会抑制编译器的非常有用的警告。

结论:明智地初始化变量。系统地做它只不过是一个货物崇拜(我工作的伙伴是可以想象的更糟糕的货物c,他从不使用goto,总是初始化变量,使用大量的静态声明(你知道它更快(它是实际上甚至在SPARC 64bit上确实很慢),即使它们有500行(在编译器不需要时使用inline)也会生成所有函数__attribute__((always_inline))

答案 6 :(得分:0)

首先,你应该初始化数组,变量等,如果不这样做会弄乱程序的正确性。

其次,似乎在这种特殊情况下,不初始化数组不会影响原始程序的正确性。相反,用于比较文件的程序对于用于判断文件是否以有意义的方式 (第一个程序定义的“有意义”)的文件格式知之甚少。

我不会抱怨原始程序,而是修复比较程序以了解有关文件格式的更多信息。如果文件格式没有很好地记录,那么你就有充分的理由抱怨。

答案 7 :(得分:0)

我想说C ++中的好习惯是使用std :: vector<>而不是数组。当然,这对C无效。