在C语言中初始化变量

24

我知道有时候如果你不初始化一个int类型的变量,在打印这个整数时你会得到一个随机数。

但是把所有东西都初始化为零好像有点儿愚蠢。

我问这个问题是因为我正在注释我的C项目,我的缩进非常直观而且它能够完全编译(谢谢Stackoverflow,90/90),但我想在样式分上得到10/10。

所以,问题来了:何时适当进行初始化,何时应该只声明一个变量:

int a = 0;

vs.

int a;
10个回答

39

有几种情况下,您不应该初始化变量:

  1. 当变量具有静态存储期(static关键字或全局变量),且您希望初始值为零时。如果您明确初始化,大多数编译器实际上会在二进制文件中存储零,这通常只是一种浪费空间(对于大型数组可能是巨大的浪费)。
  2. 当您将立即传递变量地址给另一个函数来填充其值时。在这种情况下,初始化只是浪费时间,并可能让代码读者感到困惑,他们会想知道为什么要在即将被覆盖的变量中存储某些东西。
  3. 当变量的有意义的值直到后续代码执行完成后才能确定时。在这种情况下,使用虚拟值(如零/NULL)初始化变量是有害的,因为这样会防止编译器向您发出警告,如果存在某些代码路径没有分配有意义的值,则编译器无法向您发出警告。编译器擅长警告您访问未初始化的变量,但不能警告您“仍然包含虚拟值”变量。

除了这些问题之外,我认为在可能的情况下初始化非静态变量通常是一个好习惯。


1
“巨大浪费”的情况发生在所有零对象存储在数据段而不是 bss 段时。大多数(全部?)编译器在显式初始化时都会这样做,这就是为什么我建议尽可能始终使用隐式零初始化的原因。” - R.. GitHub STOP HELPING ICE
1
我在回想80年代中期ROM系统的crt0。但现在操作系统显然使用按需零填充; 参见这个。快速测试echo'char foo [0x88888]; int main(void){return 0;}'| gcc-x c-&& readelf-a a.out | grep bss给出... .bss NOBITS ... 0888a8 ... - Joseph Quinsey
+1 对于深思熟虑的回复表示赞赏。这提供了有关变量未初始化原因的良好见解。然而,我来自相反的阵营。我总是被告知,为了避免偶尔使用随机值(并作为结果,出现不可重现的错误),必须初始化所有变量。我想给第三种情况举个反例:不幸的是,最近我经常与代码一起工作,其作者不关心警告。所以我有成百上千个这样的警告。结果我不能依赖编译器警告。在这种情况下,我更喜欢具有确定性行为而不是另一个警告。 - Dmitrii Semikin
第二种情况的另一个反例:想象一下,有一个带有“out”参数的函数,即它接受Type** pObject并将指针重置为新对象。在我看来,在这种情况下,要求*pObject == NULL是很自然的,这将更明确地表明我们使用参数作为纯“out”的意图。从理论上讲,这可能有助于找到内存泄漏(当函数被调用时,指向现有对象的指针被传递给它)。但归根结底,我不得不承认,这个讨论可能只是一种“圣战”,答案大多取决于程序员的偏好。 - Dmitrii Semikin
1
我相信代码的清晰度和可读性经常被低估。为了节省一个单词而牺牲代码维护中更高的出错率(如果有人后来使用该变量会怎么样?他/她是否启用了编译器警告?如果更新值的函数没有触及它,例如因为错误会怎样?)对我来说有点疯狂。我希望这种优化只在性能关键的部分(如软件解码器)中执行,而不是其他任何地方。(顺便说一句,许多编译器甚至会跳过代码路径中的另一个写入)。 - Narcolessico
显示剩余6条评论

32

还没有提到的一个规则是:当变量在函数内部声明时,它不会被初始化,而在静态或全局作用域中声明时,它被设置为0:

int a; // is set to 0

void foo() {
  int b;  // set to whatever happens to be in memory there
}

然而,为了可读性,我通常会在声明时初始化所有内容。

如果你对深入学习这种东西感兴趣,我推荐这个演示文稿这本书


5
我可以想到几个原因:
  1. When you're going to be initializing it later on in your code.

    int x;
    
    if(condition)
    {
        func();
        x = 2;
    }
    else
    {
       x = 3;
    }
    anotherFunc(x); // x will have been set a value no matter what
    
  2. When you need some memory to store a value set by a function or another piece of code:

    int x;  // Would be pointless to give x a value here
    scanf("%d", &x);
    

3
那段代码在g++中会生成一个警告,因为x未被初始化就被使用。 - anio

5

对于静态变量和全局变量,它们将被自动初始化为零,因此您可以跳过初始化。自动变量(例如在函数体中定义的非静态变量)可能包含垃圾值,因此应始终进行初始化。

如果需要在初始化时使用非零特定值,则应始终明确初始化。


5
如果变量是在函数范围内而不是类的成员,我总是初始化它,因为否则你会得到警告。即使这个变量稍后会被使用,我也更喜欢在声明时赋值。
至于成员变量,应该在类的构造函数中初始化它们。
对于指针,请总是将它们初始化为某个默认值,特别是NULL,即使它们稍后要使用,未初始化的指针很危险。
此外,建议使用编译器支持的最高级别的警告来构建代码,这有助于识别不良实践和潜在错误。

谢谢,我正在一个gcc/Linux shell的终端上编程(抱歉如果这些词不正确,我只是打代码然后祈求最好的结果),我们被告知使用gcc -Wall -Werror -ansi -pedantic-errors xxx.c main.c来编译,以便它可以让你知道哪些微小的错误。非常感谢您的建议! - Arthur Collé

4

初始化变量总是一个好习惯,但有时候不是严格必要的。考虑以下代码:

int a;
for (a = 0; a < 10; a++) { } // a is initialized later

或者

void myfunc(int& num) {
  num = 10;
}

int a;
myfunc(&a); // myfunc sets, but does not read, the value in a

或者

char a;
cin >> a; // perhaps the most common example in code of where
          // initialization isn't strictly necessary

这些只是几个例子,其中初始化变量并不是严格必要的,因为它稍后会被设置(但不会在声明和初始化之间访问)。

总的来说,在声明时始终初始化您的变量不会有任何损失(事实上,这可能是最佳实践)。


2

变量初始化没有任何理由不做,编译器足够聪明,如果一个变量被赋值两次,它会忽略第一次赋值。代码很容易变得庞大,你曾经认为的事情(例如在使用前赋值变量)现在已经不再成立。请考虑:

int MyVariable;
void Simplistic(int aArg){
    MyVariable=aArg;
}

//Months later:

int MyVariable;
void Simplistic(int aArg){
    MyVariable+=aArg; // Unsafe, since MyVariable was never initialized.
}

一个没问题,另一个会让你陷入麻烦。有时你的应用程序在调试模式下可以运行,但在发布模式下会抛出异常,其中一个原因是使用未初始化的变量。


简化版本的第一版是安全的。第二个版本则不是,因为它将 aArg 值添加到 MyVariable 的现有值中。MyVariable 从未被初始化,所以我们将 aArg 添加到某个未知的值上。 - anio
3
MyVariable 具有静态存储期,因此不可能未初始化。如果没有明确初始化,则其初始值为零。 - R.. GitHub STOP HELPING ICE

2

通常情况下,没有必要初始化变量,但有两个例外:

  1. 你声明了一个指针(并未立即赋值)- 应该总是将它们设置为NULL,这是良好的编程风格和防御性编程。
  2. 如果在声明变量时已经知道将分配给它的值。进一步的赋值会消耗更多的CPU周期。

除此之外,就是确保变量处于你希望执行操作的正确状态。如果你不会在操作改变变量值之前读取它们(并且操作不在乎它们的状态),那么就没有必要初始化它们。

个人而言,我总是喜欢初始化它们;如果你忘记给它赋值,并且错误地把它传递给函数(例如剩余缓冲区长度),0通常可以被清晰处理,而32532556则不能。


1
所以你建议他只有在两个条件下才需要这样做,但是又说你总是这样做,并且给出一个例子说明他为什么应该这样做? - Lou
谢谢你的回复,我很喜欢“除此之外,它还涉及将变量置于所需状态,以便执行所需操作。”这句话非常有见地。 - Arthur Collé
我从未说过这只是必要的,只是在90%的情况下,没有人会注意到差异。在这个例子中,应用程序仍然会出现问题 - 只是没有初始化,你可能会崩溃而不是提供错误的结果。良好的编程风格建议你应该初始化它们(没有强制要求你这样做),所以这就是为什么我提供了我的意见。 - septical

1
只要在写入变量之前没有读取它,我就不必担心初始化它的问题。
在写入之前读取可能会导致严重且难以捕获的错误。我认为这类错误已经够臭名昭著了,值得在流行的SICP讲座视频中提到。

这意味着在打印语句中使用变量,将其用作表达式的一部分,其结果取决于变量的值等。 写入变量意味着将一个值赋给它。 - vpit3833

0

初始化变量,即使不是严格要求,也总是一个好习惯。在开发过程中多输入一些额外字符(如“= 0”)可能会节省后续数小时的调试时间,特别是当忘记对某些变量进行初始化时。

顺便说一下,我认为将变量声明靠近其使用位置是一个好习惯。

以下是不好的:

int a;    // line 30
...
a = 0;    // line 40

以下是好的:

int a = 0;  // line 40

另外,如果变量在初始化后就要被覆盖,例如

int a = 0;
a = foo();

最好将其写成

int a = foo();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接