在 C 中抑制 'uninitialized variable warnings' 的不同方式

Different ways of suppressing 'uninitialized variable warnings' in C

我曾多次使用 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
}

Why we have two different definitions?

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

For what reason the first way may be insufficient?

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

Is the first way insufficient just for Clang or in some other cases too?

两个表达式的含义和行为都是未定义。那么,从某种意义上说,人们不能安全地得出结论,其中任何一个都足以满足任何需求。从经验的角度来看,是否使用一个或另一个会愚弄某些编译器不发出警告,否则它们会发出警告,并且仍然应该发出,很可能有,有,和/或者将是处理与这两个表达式相关的未定义行为的编译器,其处理方式不同于 GCC 和 Clang。


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

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

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

Clang 的设计大概是为了不识别 GCC 的习语,因此需要一种不同的方法。