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