【问题标题】:Different ways of suppressing 'uninitialized variable warnings' in C在 C 中抑制“未初始化变量警告”的不同方法
【发布时间】:2020-03-06 12:45:29
【问题描述】:

我遇到了 uninitialized_var() 宏的多种用途,旨在消除以下警告:

warning: ‘ptr’ is used uninitialized in this function [-Wuninitialized]

对于 GCC (<linux/compiler-gcc.h>),它是这样定义的:

/*
 * A trick to suppress uninitialized variable warning without generating any
 * code
 */
#define uninitialized_var(x) x = x

但我也发现<linux/compiler-clang.h> 以不同的方式定义了相同的宏:

#define uninitialized_var(x) x = *(&(x))

为什么我们有两种不同的定义?出于什么原因,第一种方法可能不够?第一种方法是否仅适用于 Clang 或其他一些情况?


示例:

#define uninitialized_var(x) x = x

struct some {
     int a;
     char b;
};

int main(void) {
     struct some *ptr;
     struct some *uninitialized_var(ptr2);

     if (1)
         printf("%d %d\n", ptr->a, ptr2->a); // warning about ptr, not ptr2
}

【问题讨论】:

标签: c c gcc linux-kernel initialization clang


【解决方案1】:

编译器被要求将某些构造识别为作者故意意图的指示,而编译器会警告它。例如,给定if (b = a),GCC 和 Clang 都警告分配正被用作条件,但它们不会警告 if ((b = a)),即使它在 C 标准方面是等效的。这个带有额外括号的特殊结构只是被设置为告诉编译器作者真正想要这段代码的一种方式。

同样,x = x 已设置为告诉 GCC 不要警告 x 未初始化。有时,函数可能似乎有一个代码路径,在该代码路径中使用了一个未初始化的对象,但作者知道该函数不打算与可能导致该特定问题的参数一起使用要执行的代码路径,并且出于效率的原因,他们希望使编译器警告静音,而不是添加实际上对于程序正确性而言并非必需的初始化。

Clang 可能被设计为无法识别 GCC 的习语,因此需要一种不同的方法。

【讨论】:

    【解决方案2】:

    为什么我们有两种不同的定义?

    不清楚,但我推测这是因为当x 未初始化时,Clang 仍会为x = x 生成警告,但不会为x = *(&(x)) 生成警告。在其中一个表达式具有明确定义的行为的几乎所有情况下,另一个表达式具有相同的明确定义的行为。在其他情况下,比如x的值是未定义或不确定时,两者都有未定义的行为,或者x = x的行为已定义而x = *(&(x))的行为未定义,因此后者没有优势。

    第一种方式可能不够用的原因是什么?

    因为 both 的行为在它们似乎打算用于的用例中是未定义的。因此,不同的编译器处理它们的方式不同也就不足为奇了。

    第一种方式是否仅适用于 Clang 或在某些其他情况下也不够?

    表达式的含义和行为都是未定义的。因此,从某种意义上说,人们不能有把握地得出结论,任何一个都足以解决任何问题。从经验上看,使用一种或另一种是否会使某些编译器不发出警告,否则他们会发出警告,并且仍然应该发出,很可能存在、现在和/或将要发出处理与这两个表达式相关的未定义行为的编译器与 GCC 和 Clang 不同。


    * 例外情况是当x 声明为register 存储类时,在这种情况下,无论x 是否具有明确定义的值,第二个表达式都有未定义的行为。

    【讨论】:

    • 我找不到具体的文档(因为 GCC 的文档记录很差),但 x = x 的行为并不是真正未定义的。它不是由 C 标准定义的,但 GCC 故意将其设置为一种告诉编译器不想要 x 可能 未初始化使用的警告的方法。在缺乏良好文档的情况下,我们在the documentation for -Wuninitialized 中看到了这一点的证据,它有意省略了“自初始化”。有一个 -Winit-self 开关可以改变这种行为。
    • @EricPostpischil,“未由 C 标准定义”正是我所说的“未定义”的意思,而且——众所周知——我们常客通常所说的这个词是什么意思。但是,我确实同意 GCC 记录任何自初始化的(扩展)语义,因此从这个意义上说,它与 GCC 的行为仍然是未定义的。此外,尽管我承认在实践中,GCC 区分了-Winit-self,但我认为没有任何英语理由可以解释-Wuninitialized 的“未经初始化就使用”以某种方式排除使用自初始化的特殊情况。
    • 至于脚注@EricPostpischil,当xregister 存储类声明时*(&(x)) 的行为是未定义的,因为&(x) 的行为在这种情况下是未定义的(显式从 6.5.3.2/1 中排除且未另行定义)。该确定不需要第 6.3.2.1/2 段,也不冲突。
    • Re &x 未定义:好的,您的脚注是关于使用 & 与概念上可能没有地址的东西。由于主题是未初始化的事物,因此不清楚您所说的未定义原因。
    • 重新排除自初始化的英语原因:-Wuninitialized 的文档说“如果您想警告代码在其自己的初始化程序中使用变量的未初始化值,请使用 @ 987654344@ 选项。”所以很明显自初始化被排除在外。而且,以免您认为 -Winit-self 可能是作为某个单独的选项提供的,这样您就可以请求自初始化警告而无需为未初始化的对象请求一般警告,-Winit-self 的文档说“注意此选项只能用于使用-Wuninitialized 选项。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    相关资源
    最近更新 更多