gpt4 book ai didi

C:如何迭代 `signed int` 的所有可能值,从 `INT_MIN` 到 `INT_MAX` ?

转载 作者:行者123 更新时间:2023-12-04 10:53:58 24 4
gpt4 key购买 nike

我想遍历 signed int 的所有可能值,来自 INT_MININT_MAX .明显的代码

for (int x = INT_MIN; x <= INT_MAX; x++)
do_something(x);

由于有符号整数溢出引起的未定义行为而被破坏。值得注意的是,它显然很聪明的变体也是如此
int x = INT_MIN;
do {
do_something(x);
} while (x++ < INT_MAX);

有趣的是,在第二个例子中 clang -O2 -fsanitize=undefined正确报告 runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' , 而 gcc -O2 -fsanitize=undefined才不是。我想 gcc-fsanitize=signed-integer-overflow碎成 -ftrapv .

我能找到的表达此迭代的唯一方法是使用 goto :
int x = INT_MIN;
loop: {
do_something(x);
if (x < INT_MAX) {
x++;
goto loop;
}
}

使用无限循环和 break手动输入:
int x = INT_MIN;
while (1) {
do_something(x);
if (x < INT_MAX)
x++;
else
break;
}

并仅在安全的情况下利用短路评估来增加变量:
int x = INT_MIN;
do {
do_something(x);
} while (x < INT_MAX && ++x != INT_MIN);

正如 Steve Jessop 所指出的,在 do-while一种可以使用的解决方案 while (x < INT_MAX && (++x, 1));反而。

三个版本中,我不是特别反对 goto版本,我不喜欢 do-while版本非常多(尤其是 ++x != INT_MIN 位...),但总的来说我更喜欢 while (1)版本。我的问题如下。

是否允许 C 编译器完全消除无限while (1)万一循环 do_something不执行输入/输出操作,也不访问 volatile 对象?

我知道它不能用于 C11 ,但我对以前的版本不太确定。

编辑

我将尝试更详细地描述我担心的事情。假设我正在使用这个循环来验证其他一些代码的正确性。例如,我可能会这样做
#include <stdio.h>
#include <limits.h>

int add_wrap_unsigned(int x, int y) {
return (unsigned) x + (unsigned) y;
}

int add_wrap_builtin(int x, int y) {
int res;
__builtin_add_overflow(x, y, &res);
return res;
}

int check() {
int x = INT_MIN;
while (1) {
if (add_wrap_unsigned(x, 1) != add_wrap_builtin(x, 1))
return 1;
if (x < INT_MAX)
x++;
else
break;
}
return 0;
}

int main() {
if (check())
printf("counterexample found");
else
printf("no counterexample");
return 0;
}

这正确报告 no counterexampleclang . check被编译成一个简单的 return 0 .但是,更改单行(第 22 行从 break;x = INT_MIN; ) changes completely the behavior : check变成 noop 无限循环,但 main现在打印 counterexample found .即使使用 -std=c11,这一切也会发生.

我担心的是我不能相信第一个版本,因为编译器可能没有做正确的事情,就像在第二种情况下一样。

最佳答案

问题是是否允许 C 编译器删除无限循环。不,不允许删除控制表达式为常量( for (;;) {}while (1) { } )的无限循环,但 C 标准 明确说明 C11/C17 6.8.5p6

  1. An iteration statement whose controlling expression is not a constant expression,156) that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.157)


IE。允许编译器优化没有任何副作用并且确实具有非常量控制表达式的循环。 IE。当涉及到 as-if 规则时,这是一个完全无用的循环。此异常(exception)已在标准的 C11 修订版中授予。这在 C11 之前并不适用,因此如果存在该行为,则它不符合标准。

您不需要保护增量,只需 break :
for (int x = INT_MIN; ; x++) {
do_something(x);
if (x == INT_MAX) break;
}

在这种情况下,控制表达式是一个常量表达式(隐式真值),因此 不得按照 C11/C17 6.8.5p6 进行优化,但编译器必须明确证明它才能终止。

现在优化到什么取决于编译器和标志 - 使用 -O3 Clang 9.0将其编译为相当于
for (int x = INT_MIN; x != INT_MIN; x++)
do_something(x)

如果溢出是用环绕定义的——但正如我们所知,x86 机器代码确实有明确定义的环绕。

GCC 9.2产生的机器码的行为类似于
int x = INT_MIN;
do_something(x);
do {
do_something(++x);
} while (x < INT_MAX);

在 C 中这样写的将是 没有未定义的行为 并且在性能上与 Clang 几乎没有区别(但在大小上没有区别)。

关于C:如何迭代 `signed int` 的所有可能值,从 `INT_MIN` 到 `INT_MAX` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59327636/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com