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

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

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

这让我提出了一个问题,即在C / C ++中初始化数组是否也是一个好习惯,因为它是用Java完成的?

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

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

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

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

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

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

不要忘记,对于庞大的PODarrays,有一个很好的简写,可以将所有成员初始化为零

 MyPODStruct bigArray[1000] = { 0 }; 

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

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

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

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

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

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

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

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

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

 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的怪),所以我们在项目的最热function中有3个副本+一个隐藏的函数调用。 将其重写为

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

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

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

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

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

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

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

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