【问题标题】:Initialize all variables, always始终初始化所有变量
【发布时间】:2013-05-10 09:19:00
【问题描述】:

我正在阅读the FreeBSD coding style 并且非常喜欢它(因为我喜欢垂直紧凑的代码)。但是有这样的:

初始化所有变量
您应始终初始化变量。总是。每次。带有标志 -W 的 gcc 可能会捕获对未初始化变量的操作,但是 也可能不会。

理由
比你想象的更多的问题最终被追溯到未初始化的指针或变量。

当变量没有合适的初始值时,让它没有值不是更好吗?这样编译器可能会在未初始化的情况下读取它。我不是在谈论T *p = NULL,这是一个陷阱表示,可能(或可能不是)非常有用,而是int personal_number = 0 /* but 0 is a valid personal number!!*/


为了澄清,针对 abasu 的评论,我的示例试图说明没有可用无效值的情况。我问了question,得到的回答是使用不可能的值来标记错误或其他条件非常棒。但情况并非总是如此。例子很多:8bit像素值、速度向量等。


我可以看到“始终初始化变量”的一种有效替代方法是:

//logical place for declarations
T a;

/*code, for example to set up the environment for evaluating a*/

a = fooForA();

/*more code*/

fooThatUsesA(a);

这样如果忘记初始化,会出现警告并修复bug,消除警告。

【问题讨论】:

  • 取决于什么是有意义的... -1 作为无效的personal_number 怎么样?
  • 如果它也未初始化,这并不意味着不会有值。并且该值也可以是有效数字(偶然地,意外地)。那么你的选择是什么?当具有某些有效值的未初始​​化变量产生一些不需要的副作用时,让它走运并可能面临灾难?或者花大约 10 秒找出一个无效值,并确保你的变量是用它初始化的(当然你的程序正在检查它)
  • @abasu,我的意思是编译器或静态分析器很容易检测到未初始化的值。另一方面,将变量设置为 错误 有效 值可能会让程序员和机器感到困惑。
  • @Vorac 好的。编译器捕获它未初始化并给你一个警告。这很棒。 然后你会怎么做? “始终初始化变量”与利用该编译器警告并不矛盾。实际上,存在警告是因为您首先违反了规则。问题是您将如何解决该违规行为。或者您是否在代码中看到警告说您有未初始化的变量只是为了看到警告?如果您实际上不一些事情,那是没有意义的。
  • @Vorac 是的,你的观点是有效的。 (虽然我不确定,编译器和静态分析器可以捕获 100% 的情况,为此,有运行时工具)但是第二行,setting a variable to a wrong valid value 这不是一个错误(即编码错误??)。如果是,那么会出现混乱。预期的不是问题,问题是出乎意料的。 :) :) (个人经验)

标签: c coding-style


【解决方案1】:

所有个整数都是有效的个人号码吗?

如果不是,则使用无效值初始化personal_number

如果是,那么即使您自己没有初始化personal_number它仍然拥有一个有效的个人号码——但该值是未知的。所以无论如何都要将它初始化为0——你没有引入问题(之前的有效数字,之后的有效数字),唯一的区别是你现在知道了这个数字。

当然,在这两种情况下,最好不要使用整数文字进行初始化,而是这样做:

enum { INVALID_PERSONAL_NUMBER = -1 }

int personal_number = INVALID_PERSONAL_NUMBER;

【讨论】:

  • 您假设变量存在无效值(因为我的例子不好)。在某些情况下,情况并非如此。我对它们很感兴趣。
  • @Vorac 他没有假设任何事情。他给了你我认为在这两种情况下最好的答案:如果不是(...),如果他们是(...)。
  • @Vorac:我确实没有假设。您在上面评论了“设置为错误的有效值”——您必须意识到,就目前而言,它已经设置为错误的有效值(由编译器)。
  • 是的,我也最喜欢这个答案。然而,虽然您的观点完全正确,但我们仍然失去了在编译时而不是在运行时使用静态分析来捕获错误的能力。我认为真相介于您的答案和 JeremyP 的答案之间。让问题暂时保持开放。
  • @Vorac 你已经发现了这个错误。您找到了一个未初始化的变量。你还想要什么?
【解决方案2】:

编译器通常不会捕获未初始化的读取变量。相反,他们可能会使用该信息对其余代码进行假设以执行优化,这可能会引入新的和更严重的错误:

int get_personal_number(const char *name)
{
    int personal_number;
    if (name != NULL) {
        /* look up name in some array */
        personal_number = ...
    }
    return personal_number;
}

优化编译器将推断 name 不能是NULL 并消除检查。类似的问题导致了安全漏洞;参见例如http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html

相反,重写你的函数以在声明时使用它们最终正确的值来初始化变量;这可能需要编写许多小函数,使用三元表达式等,这通常是更好的风格。

【讨论】:

  • 如果指针之前没有取消引用,为什么编译器会消除 NULL 检查?
【解决方案3】:

当变量没有合适的初始值时,让它没有值不是更好吗?

在我看来是的。现代编译器非常擅长捕捉未初始化的变量错误,而 clang 静态分析器几乎完美得令人毛骨悚然。让编译器捕获问题比放入会导致运行时问题的东西要好得多。例如,初始化指向 NULL 的指针会抑制编译器警告,但在您尝试取消引用它时不会停止核心转储。

然而,如果您使用的是现代编译器,您可能使用的是 C99,这意味着您无需声明变量,直到您知道它的合理值。所以这就是我会做的。

【讨论】:

  • 恕我直言,这是我问题正确答案的一半。暂时让这个问题悬而未决。顺便说一句,C89 固执地坚持为声明引入新块,这对于强制对变量进行范围限制非常有用,即每次都精确地声明每个变量的范围。
  • @Vorac 另一半是什么?我会看看我是否也能解决这个问题。
【解决方案4】:

初始化变量总是有用的,是一种很好的编码习惯。 这个例子可以理解:

一个未初始化的变量将包含一些垃圾值。如果您没有初始化它并且错误地尝试使用它。你可能会得到一些意想不到的结果。 例如:

int test(void)
{
    int a; //uninitialized variable

    //You didn't initialize a  
    if(a > 10) 
    {
          //Unpredicted result
    }
    else{}
    return 0;
}

在大型程序中情况会变得很严重,这些类型的失误很常见。 所以为了避免愚蠢的错误,否则可能会在调试它们时花费大量时间,变量应该总是被初始化

【讨论】:

  • 如果你像往常一样打开-Wall,警告会告诉你未初始化的a。另一方面,如果你猜到了一个错误的值,你稍后会得到一个运行时错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-04
  • 2021-06-21
  • 2019-07-12
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多