【问题标题】:C: type-limits is limited to 0 constant valueC: type-limits 被限制为 0 常量值
【发布时间】:2021-09-27 22:36:14
【问题描述】:

我正在尝试了解我的 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)

【问题讨论】:

  • 给定 unslgned long ulul 怎么可能小于零或大于 ULONG_MAX
  • 问题是:Why is the type-limits warning limited to the 0 constant value[...]
  • 警告通常是可选的——它们是编译器编写者花更多时间告诉你的,“嘿,我们认为这很狡猾。你可能需要重新考虑你的代码。”如果 C 标准中没有要求发出诊断,则不应期望生成诊断。
  • Why is the type-limits warning limited to the 0 constant value, and does not handle ULONG_MAX ? 没有具体原因,有人这样写编译器。
  • @MicroVirus 为什么不将其发布为答案?

标签: c gcc clang c99


【解决方案1】:

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

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

GCC 的文档比 Clang 的要好得多,所以我以它为例。 Its documentation for -Wtype-limits 说,

由于有限的限制,比较总是真或总是假时发出警告 数据类型的范围,但不警告常量表达式。为了 例如,如果用 =。

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

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

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

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

【讨论】:

  • 我最初对“类型限制”这个词感到困惑,现在它在符号性的上下文中是有意义的。谢谢
【解决方案2】:

关于与 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);
}

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

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

【讨论】:

  • 我真的很喜欢这个例子,让它简单易懂。谢谢
猜你喜欢
  • 1970-01-01
  • 2015-03-25
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-19
  • 1970-01-01
  • 2018-12-25
相关资源
最近更新 更多