C: type-limits 被限制为 0 常量值

C: type-limits is limited to 0 constant value

我试图了解我的 gcc 设置中的以下行为:

% cat t.c
#include <limits.h>

// -Wtype-limits
int type_limits1(unsigned long ul) {
  return ul >= 0;
}

int type_limits2(unsigned long ul) {
  return ul <= ULONG_MAX;
}

导致:

% gcc  -c -std=gnu99 -Wtype-limits  -x c t.c
t.c: In function ‘type_limits1’:
t.c:5:13: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
   return ul >= 0;
             ^~

为什么类型限制警告 仅限于 0 常量值 ,而不处理 ULONG_MAX?这是因为 0 是语言的一部分,而 #define ULONG_MAX 不是(我猜有一个很好的理由不实施,我只是看不出是哪一个)。

我可以使用 clang 重现相同的行为:

% clang  -c -std=gnu99  -Weverything -Wno-missing-prototypes -x c t.c
t.c:5:13: warning: result of comparison of unsigned expression >= 0 is always true [-Wtautological-unsigned-zero-compare]
  return ul >= 0;
         ~~ ^  ~
1 warning generated.

使用:

% gcc --version
gcc (Debian 8.3.0-6) 8.3.0

% clang --version
clang version 7.0.1-8+deb10u2 (tags/RELEASE_701/final)

关于与 0 进行无用比较的警告比关于与类型最大值进行无用比较的警告有用得多,因为它的真阳性可能更重要,而且误报更少。

将值与 0 进行比较通常意味着存在值将为 0 的有用情况。例如,循环倒计时到 0 是一个相当常见的习惯用法,但如果循环计数器是未签名且终止检查发生在循环体之前。

unsigned i;
for (i = n; i >= 0; i--) do_something(i);

这是一个无限循环,但显然不是故意的,所以编译器抱怨是件好事。

没有那么多误报。代码在不知道其大小的情况下处理整数类型很常见,但处理未知符号的整数类型并不常见。

将一个值与一个较大的数字进行比较,例如 <em>type</em>_MAX 通常是范围检查,如果值较大,则程序将其视为错误条件。如果条件恰好是不可能的,这完全没问题。

相反,在处理多种未知大小的整数类型的代码中,警告与最大值的无用比较会很烦人。例如:

unsigned long producer(void);
void consumer(size_t x);
void middle() {
    unsigned long x = producer();
    if (x > SIZE_MAX) fatal_error();
    consumer(x);
}

生产者returns整数类型的值。它需要传递给需要不同整数类型的消费者。根据平台的不同,生产者的范围可能适合也可能不适合消费者的范围。

诚然,将此特定检查包装在预处理器条件 #if SIZE_MAX > ULONG_MAX 中很容易。这仅在存在用于所涉及类型限制的预处理器常量时才有效。如果您所知道的只是您有两个无符号类型,并且知道它们上限的唯一方法是 (type_t)-1,预处理器将无能为力。

有一组相对较小的代码形式,语言规范要求编译器发出诊断消息:违反规​​则标记为语言约束。该规范没有对这些消息的形式提出具体要求,它既不要求也不禁止任何其他消息传递。

所提供的代码似乎严格符合标准。它满足规范的所有要求,并且不执行任何未定义或实现定义的行为。因此,语言规范根本没有说明实现在处理它时可以或应该发出什么消息。这完全取决于实现。

GCC 的文档比 Clang 的文档好得多,所以我将以此为范例。 Its documentation 对于 -Wtype-limits 说,

Warn if a comparison is always true or always false due to the limited range of the data type, but do not warn for constant expressions. For example, warn if an unsigned variable is compared against zero with < or >=.

请注意,它特别指出了将无符号类型的值与 0 进行比较的情况,因此当 GCC 使用 -Wtype-limits 处理您的代码时发出警告也就不足为奇了效果。

手册的措辞确实表明,在将 unsigned longULONG_MAX 进行比较的情况下,也可能会出现警告。我只能推测为什么 GCC 不警告这种情况,但肯定是更温和的。与零常量的关系比较表明程序员可能认为另一个操作数是有符号的。如果它是未签名的,但程序员认为它是已签名的,那么很可能会出现错误。我一直把它作为这个特别警告的灵感。

与类型范围上限的比较不太可能是编程错误。通常,它们还取决于所涉及的实际限制,因此它们是代码和 C 实现的特征,而不仅仅是代码的特征。您的特定情况使用 limit 宏,从而避免了实现依赖性,但考虑到编译器希望在预处理后进行此类评估,因此在决定警告时您的代码细节可能不可用。

不过,归根结底,这只是一个实施决定。