我看到的每个地方都有人大声争辩说未经初始化的变量是坏的,我当然同意并理解为什么 - 然而;我的问题是,有时候你不想这样做吗?
例如,取代码:
char arrBuffer[1024] = { '\0' };
对整个阵列执行NULL操作是否会在不初始化的情况下使用阵列产生性能影响?
答案 0 :(得分:10)
我假设堆栈初始化,因为静态数组是自动初始化的 G ++输出
char whatever[2567] = {'\0'};
8048530: 8d 95 f5 f5 ff ff lea -0xa0b(%ebp),%edx
8048536: b8 07 0a 00 00 mov $0xa07,%eax
804853b: 89 44 24 08 mov %eax,0x8(%esp)
804853f: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8048546: 00
8048547: 89 14 24 mov %edx,(%esp)
804854a: e8 b9 fe ff ff call 8048408 <memset@plt>
因此,您使用{'\ 0'}进行初始化并调用memset,所以是的,您的性能会受到影响。
答案 1 :(得分:7)
如果变量是全局变量或静态变量,则其数据通常逐字存储在已编译的可执行文件中。因此,您的char arrBuffer[1024]
会将可执行文件大小增加1024个字节。初始化它将确保可执行文件包含您的数据,而不是默认的0或编译器选择的任何内容。程序启动时,无需进行任何处理即可初始化变量。
另一方面,堆栈上的变量(例如非静态本地函数变量)不会以相同的方式存储在可执行文件中。相反,在函数入口上,空间被分配在堆栈上,memcpy将数据放入变量中,从而影响性能。
答案 2 :(得分:4)
规则是变量应该在使用之前设置。
如果您知道在使用之前将其他地方设置为 ,则 必须在创建时显式初始化它们。
例如,以下代码完全没问题:
int main (void) {
int a[1000];
: :
for (int i =0; i < sizeof(a)/sizeof(*a); i++)
a[i] = i;
: :
// Now use a[whatever] here.
: :
return 0;
}
在这种情况下,在创建数组时初始化数组会很浪费。
关于是否存在性能损失,它部分取决于您的变量的定义位置,部分取决于执行环境。
C标准保证用静态存储持续时间定义的变量(在文件级别或在函数中作为静态)首先被初始化为全零的位模式,然后设置为它们各自的初始化值。
它不要求如何完成第二步。一种典型的方法是让编译器本身创建初始化变量并将其放在可执行文件中,以便通过加载可执行文件来初始化它。这对性能没有影响(对于初始化,显然它会对程序负载产生一些影响)。
当然,实现可能希望节省可执行文件中的空间并使用代码初始化这些变量(在调用main之前)。 会对性能产生影响,但可能会很小。
对于那些具有自动存储持续时间的变量(局部变量等),除非你为它们分配东西,否则它们永远不会被隐式初始化,因此也会有性能损失。通过“从不隐式初始化”,我的意思是代码段:
void x(void) {
int x[1000];
...
}
将导致x []具有不确定的值。但是从那以后:
void x(void) {
int x[1000] = {0};
}
可能只会导致1000整数的memcpy类型操作(对于那种情况更可能是memset),这也可能很快。您只需要记住,初始化将发生每个时间调用该函数。
答案 3 :(得分:2)
测量!
#include <stdio.h>
#include <time.h>
int main(void) {
clock_t t0;
int k;
t0 = clock();
for (k=0; k<1000000; k++) {
int a[1000];
a[420] = 420;
}
printf("Without init: %f secs\n", (double)(clock() - t0) / CLOCKS_PER_SEC);
t0 = clock();
for (k=0; k<1000000; k++) {
int a[1000] = {0};
a[420] = 420;
}
printf(" With init: %f secs\n", (double)(clock() - t0) / CLOCKS_PER_SEC);
return 0;
}
$ gcc measure.c $ ./a.out Without init: 0.000000 secs With init: 0.280000 secs $ gcc -O2 measure.c $ ./a.out Without init: 0.000000 secs With init: 0.000000 secs
答案 4 :(得分:0)
对于大型阵列,性能影响可能很大。默认情况下,所有变量的初始化实际上并没有带来很多好处。它不是坏代码的解决方案,而且它可能隐藏可以被编译器捕获的实际问题。您需要在整个生命周期内跟踪所有变量的状态,以使代码可靠。
答案 5 :(得分:0)
要回答您的问题:可能会对性能产生影响。编译器可能会检测到数组的值未使用而只是不执行它们。这是可能的。
我个人认为这是个人风格的问题。我很想说:保持未经初始化,并使用类似Lint的工具告诉你,如果你使用它未经初始化,这肯定是一个错误(而不是使用默认值而不被告知,这也是一个错误,但是一个无声的。)
答案 6 :(得分:0)
我认为要求所有变量在声明时默认初始化是一个不好的建议。在大多数情况下,这是不必要的,并且会带来性能损失。
例如,我经常使用下面的代码将数字转换为字符串:
char s[24];
sprintf(s, "%d", int_val);
我不会写:
char s[24] = "\0";
sprintf(s, "%d", int_val);
现代编译器能够判断是否在未初始化的情况下使用变量。
答案 7 :(得分:0)
您的变量应初始化为有意义的值。盲目而天真地将一切设置为零并不比将其保持未初始化好多少。它可能会导致无效代码崩溃,而不是表现不可预测,但它不会使代码正确。
如果您在创建数组时只是天真地将数组清零以避免未初始化的变量,那么它仍然逻辑未初始化。它还没有在您的应用程序中有意义的值。
如果您要初始化变量(并且您应该),请为它们提供在您的应用程序中有意义的值。你的其余代码是否期望数组最初为零?如果是,请将其设置为零。否则将其设置为其他有意义的值。
或者如果你的代码的其余部分希望写入数组,而不先读取它,那么一定要保持未初始化。
答案 8 :(得分:0)
我个人反对在创建时初始化一个数组。请考虑以下两段代码。
char buffer[1024] = {0};
for (int i = 0; i < 1000000; ++i)
{
// Use buffer
}
VS
for (int i = 0; i < 1000000; ++i)
{
char buffer[1024] = {0};
// Use buffer
}
在第一个例子中为什么要打扰初始化缓冲区,因为循环缓冲区周围的第二次不再是0初始化?除了第一次迭代之外,我对缓冲区的使用必须在没有被初始化的情况下工作。所有的初始化都消耗时间,膨胀代码并掩盖错误,如果通常我只通过循环一次。
虽然我当然可以将代码重新考虑为第二个例子,但是如果我可以重新编写代码而不是必要的话,我是否真的想在循环内初始化缓冲区?
我怀疑这些天大多数编译器都有选项来填充非0值的未初始化变量。我们以这种方式运行所有调试版本,以帮助检测未初始化变量的使用,并且在发布模式下,我们关闭选项,以便变量真正未初始化。正如Sherwood Hu所说,一些编译器可以注入代码来帮助检测未初始化变量的使用。
编辑: 在上面的代码中,我将缓冲区初始化为值0(不是字符“0”),这相当于用'\ 0'初始化它。
为了进一步澄清我的第一个代码段,想象一下下面的设计示例。
char buffer[1024] = {0};
for (int i = 0; i < 1000000; ++i)
{
// Buffer is 0 initialized, so it is fine to call strlen
int len = strlen (buffer);
memset (buffer, 'a', 1024);
}
第一次通过循环时缓冲区被初始化为0,因此strlen将返回0.第二次通过循环时缓冲区不再初始化为0,实际上不包含单个0字符,所以strlen的行为未定义。
既然你已同意我的说法,如果初始化了缓冲区,那么不建议在循环内部移动缓冲区,并且我已经证明在循环外部初始化它不提供保护,为什么要初始化它?
答案 9 :(得分:-2)
为什么你要关心性能优势,通过不初始化它会获得多少性能,并且由于垃圾指针而不是在调试期间节省的时间。