C++ 当(i--)s+;=a[我];在C和C+中包含未定义的行为+;?

C++ 当(i--)s+;=a[我];在C和C+中包含未定义的行为+;?,c++,c,undefined-behavior,C++,C,Undefined Behavior,考虑简单的代码: #include "stdio.h" #define N 10U int main() { int a[N] = {0}; unsigned int i = N; int s = 0; // Fill a while(i--) s += a[i]; printf("Sum is %d\n", s); return 0; } 由于整数下溢,而循环是否包含未定义的行为?编译器是否有权假定while

考虑简单的代码:

#include "stdio.h"

#define N 10U

int main() {
    int a[N] = {0};
    unsigned int i = N;
    int s = 0;

    // Fill a

    while(i--)
        s += a[i];

    printf("Sum is %d\n", s);

    return 0;
}
由于整数下溢,
循环是否包含未定义的行为?编译器是否有权假定
while
循环条件始终为真,并最终导致无休止的循环

如果
i
signed int
?它不包含与阵列访问相关的陷阱吗

更新

我多次运行这个和类似的代码,效果很好。此外,它是向后迭代数组和向量的常用方法。我问这个问题是为了确保从标准的角度来看,这种方法是可行的


乍一看,它显然不是无限的。另一方面,有时编译器可以“优化”掉一些条件和代码,假设代码不包含未定义的行为。它可能导致无限循环和其他不必要的后果。请参阅。

此代码不会调用未定义的行为。当
i
变为
0
时,循环将终止


对于
无符号int
,没有整数溢出/下溢。
i
的效果与
signed
相同,只是在这种情况下没有包装。

i
将包装到
~0
0XFFFFFFFF
用于32位),但循环将终止,因此没有UB

while循环是否因整数下溢而包含未定义的行为

否,对于有符号整数,溢出/下溢只是未定义的行为

编译器是否有权假设while循环条件始终为真,并最终导致无休止的循环

不,因为表达式最终会变成零

如果我是int呢?它不包含与阵列访问相关的陷阱吗


如果它是有符号的,并且在流上/流下,则调用未定义的行为。

由于以下原因,循环不会产生未定义的行为

i
初始化为
10
,并在循环中递减。当
i
的值为零时,将其减小将产生一个等于
UINT_MAX
的值,该值是
无符号
可以表示的最大值,但循环将终止<对于
N-1
(即
9
)和
0
之间的
i
值,只能访问[i]
(在循环内)。这些都是数组
a
中的有效索引

s
a
的所有元素都初始化为零。因此,所有的添加将
0
添加到
0
。这不会使
int
溢出或下溢,因此不会导致未定义的行为

如果将
i
更改为
signed int
,则当循环终止时,递减永不下溢和
i
将具有负值。唯一的净变化是
i
在循环后的值。

while循环是否因为整数下溢而包含未定义的行为? 首先,这是一个:

整型、浮点型、非范围枚举型、指针型和指向成员类型的指针型prvalue可以转换为类型为
bool


值零(用于整数、浮点和非范围枚举)以及空指针和指向成员值的空指针变为
false
。所有其他值变为

因此,如果正确初始化
i
,则不会出现整数下溢,它将达到
0U
,并将转换为
false

因此,编译器在这里有效地做的是:
while(static_cast(i--))

编译器是否有权假设while循环条件总是
true
,并因此而以无休止的循环结束? 这不是无止境循环的关键原因是返回递减变量的值。它被定义为
T运算符--(T&,int)
,如前所述,编译器将在转换为
bool
的过程中评估该值是否为
0U

编译器在这里有效地做的是:
while(0!=运算符--(i,1))

在更新中,您引用此代码作为问题的动机:

void fn(void)
{
    /* write something after this comment so that the program output is 10 */
    int a[1] = {0};
    int j = 0;
    while(a[j] != 5) ++j;  /* Search stack until you find 5 */
    a[j] = 10;             /* Overwrite it with 10 */
    /* write something before this comment */
}
检查后,整个程序有未定义的行为,只有
a
的1个元素,它被初始化为0。因此,对于除
0
之外的任何索引,
a[j]
都从数组的末尾向外看。将继续,直到找到
5
,或者由于程序已从受保护内存中读取而导致操作系统出现错误。这不同于当后缀减量运算符返回值0时将退出的循环条件,因此不能假设这总是
true
,或者循环将无限继续

如果
i
signed int
? <>这两种转换都是C++内置的。它们都是为所有整数类型定义的,有符号和无符号。因此,这最终扩展到:

while(static_cast<bool>(operator--(i, 1)))
while(静态_-cast(运算符--(i,1)))
它不包含与阵列访问相关的陷阱吗? 这也是在调用一个内置操作符:
T&operator[](T*,std::ptrdiff\u T)

因此
a[i]
相当于调用
操作符(a,static_cast(i))


所以接下来的问题是什么是a?它是一个实现定义的整数,但同样地,标准的每个实现都负责定义与此类型之间的转换,因此
i
将正确地强制转换。

您的代码表现良好,对于任何值N,都不包含未定义的行为≥ 0

让我们关注while循环,并跟踪N=2的执行示例

// Initialization
#define N 2U
unsigned int i = N;  // i = 2

// First iteration
while(i--)  // condition = i = 2 = true; i post-decremented to 1
    s += a[i];  // i = 1 is in bounds

// Second iteration
while(i--)  // condition = i = 1 = true; i post-decremented to 0
    s += a[i];  // i = 0 is in bounds

// Third iteration
while(i--)  // condition = i = 0 = false; i post-decremented to 0xFFFFFFFF
// Loop terminated
我们可以从这个跟踪中看到,
a[i]
总是在边界内,而
i