进一步调查发现,有一个结构包含一些整数和一个64字节的字符数组,在大多数情况下未使用字符数组的所有字节,并且数组中的未使用字段包含随机数据,这导致了不匹配。
这让我想问一个问题,是否在C / C ++中初始化数组也像Java一样是良好的实践?
在使用变量之前对内存/变量进行初始化是一个好的实践 - 未初始化的变量是 bug 的主要来源,往往非常难以追踪。
当将数据写入文件格式时,初始化所有数据是一个非常好的想法:它使文件内容更加清晰易读,如果有人错误地尝试“使用”未初始化的数据(请记住,可能不仅仅是你自己的代码读取数据),那么就不容易出现问题,并且使文件更易于压缩。
唯一不初始化变量的好理由是在性能关键的情况下,其中初始化在技术上是“不必要”的并会产生显着的开销。但在大多数情况下,初始化变量不会造成显着的损害(特别是如果它们只是在使用之前立即声明),但它会通过消除常见的 bug 来节省您大量的开发时间。
MyStruct array[10];
printf( "%f", array[2].v ); // POTENTIAL BANG!
array[3].v = 7.0;
...
printf( "%f", array[3].v ); // THIS IS OK.
MyPODStruct bigArray[1000] = { 0 };
有关两种不同的编程风格,即在声明变量时初始化和在必要时初始化,可以写一篇大文章。我与一个始终在声明变量时初始化的人共同完成了一个大项目,而我现在明确更倾向于第二种类型。 始终初始化变量比不初始化带来了更多微妙的错误和问题,我将尝试解释为什么,并回忆我发现的情况。 第一个例子:
struct NODE Pop(STACK * Stack)
{
struct NODE node = EMPTY_STACK;
if(Stack && Stack->stackPointer)
node = Stack->node[--Stack->stackPointer];
return node;
}
memcpy
,以及结构体的另外两个副本(顺便说一下,它们并不是对memcpy
的调用,gcc有时候很奇怪),所以在项目中最热门的函数中有3个副本+一个隐藏的函数调用。
重写它为
struct NODE Pop(STACK * Stack)
{
if(Stack && Stack->stackPointer)
return Stack->node[--Stack->stackPointer];
return EMPTY_STACK;
}
只有一份副本(在SPARC上运行时具有补充优势,该函数是叶子函数,由于避免了对memcpy
的调用,因此不需要构建新的寄存器窗口)。 因此,该函数速度提高了4倍。
我曾经发现另一个问题,但不记得确切的位置(所以没有代码示例,抱歉)。一个变量在声明时被初始化,但在循环中使用,在有限状态自动机中使用switch
。问题是初始化值不是自动机的状态之一,在极其罕见的情况下,自动机无法正常工作。通过删除初始化程序,编译器发出的警告表明变量可能在正确初始化之前就被使用。然后很容易修复自动机。
道德:防御性地初始化变量可能会抑制编译器非常有用的警告。
inline
(使用__attribute__((always_inline))
当编译器不想要时)
如果您不初始化c++数组中的值,那么这些值可能是任何值,因此如果您想要可预测的结果,最好将它们清零。
但是,如果您像使用空终止字符串一样使用char数组,则应该能够使用适当的函数将其写入文件。
尽管在c++中,使用更多面向对象的解决方案可能会更好。例如:向量、字符串等。
记住,保持数组未初始化可能具有性能优势。
只有从未初始化的位置读取数组才是不好的。如果它们一直存在而从未从未初始化的位置读取,则没有问题。
此外,如果您的程序存在错误,使其从数组中未初始化的位置读取,则通过防御性地将所有数组初始化为已知值来“掩盖”它并不是解决错误的方法,只会使其在以后浮出水面。
我认为在C++中的良好实践是使用std::vector<>而不是数组。当然,这对于C语言无效。