提问人:Thomas Baruchel 提问时间:11/14/2023 更新时间:11/14/2023 访问量:121
这个两增量表达式在 C 中定义得很清楚吗?
Is this two-increments expression well-defined in C?
问:
我想通过跳过所有具有特定位集的整数来遍历整数的 2 的幂。假设是 2 的某种幂(小于 ),我想跳过所有整数,以便 .我喜欢下面的 -loop,但我想知道它是否根据标准完美定义:t
m
t
j
j&m != 0
for
for (int j=0; j < t; j += (++j) & m) {
...
}
这个想法是:
- increment with 用于遍历所有整数
j
++j
- 每当前一个增量设置了权重位时,也添加跳过整个块。
m
m
但是,由于同一表达式中有两个增量,因此我想确保它是完全正确的。
答:
这个程序的行为没有被定义为 (UB) *(谢谢 Eric)*在修改和分配之间没有序列点没有序列。
许多编译器会发出警告:
warning: unsequenced modification and access to 'j' [-Wunsequenced]
| for (int j=0; j < t; j += (++j) & m) {
即使没问题,我也强烈建议你不要写这种“hacky”表达式,因为它们对人类来说很难阅读,使得代码很难理解、调试和维护
该表达式调用未定义的行为,因为它包含对 的多个未序列写入。j += (++j) & m
j
与其试图想出一个“聪明”的增量,不如在语句中执行增量并在循环中执行检查。for
for (int j=0; j < t; ++j) {
if ((j&m) != 0) {
// skip ahead
j += m - 1;
continue;
}
...
}
评论
m
continue
如前所述,您尝试使用的表达式具有未定义的行为,因为 和 是未排序的。但是,您对要执行的操作的描述表明,使用应该执行所需的操作,而不会出现任何未定义的行为。++
+=
j += 1 + ((j + 1) & m)
#include <stdio.h>
int main(void)
{
int m = 0x8;
int t = 33;
for (int j = 0; j < t; j += 1 + ((j + 1) & m))
printf("%d\n", j);
return 0;
}
输出:
0
1
2
3
4
5
6
7
16
17
18
19
20
21
22
23
32
我认为这符合你的描述。因为是 8,所以它会立即跳过值 8..15 和 24..31,它们是 0..32 范围内设置了位的值。m
请注意,仅当 的初始值不包括 标识的位时,这才有效。如果将 start 从 更改为 ,则得到输出 、 。当然,可以修改代码来处理这个问题,但这有点棘手,确切地说如何做到这一点取决于尚未说明的要求(预期的输出是什么?j
m
0
9
9
18..23
32
旁注:如果您的问题包括您期望的输出的示例输出,这将很有帮助,类似于我提供的内容。请阅读有关如何创建 MCVE(最小、完整、可验证示例 — 或 MRE 或 SO 现在使用的任何名称)的信息 或 SSCCE(简短、自包含、正确的示例——相同的想法只是不同的名称)。
这是未定义的行为。该标准的相关部分是:
C17 6.5 §2(表达式):
如果标量对象上的副作用相对于 相同的标量对象或使用同一标量对象的值进行值计算,行为 未定义。如果表达式的子表达式有多个允许的顺序,则 如果在任何排序中发生这种未排序的副作用,则行为是未定义的。
C17 6.5.16 §3(赋值运算符):
旁边 更新左操作数的存储值的效果在值计算之后排序 左操作数和右操作数。操作数的计算是未排序的。
C17 6.5.16.2 §3(复合分配):
形式的复合赋值等价于简单赋值表达式,只是左值 E1 只计算一次
E1 op= E2
E1 = E1 op (E2)
基于此:
一些味道是UB。
表达式等效于 。值计算与同一对象的副作用有关是无序的。UB根据“同一对象的副作用未排序的相对值计算”。j + ++j
j += (++j) & m
j = j + ((++j) & m)
j +...
++j
一些味道是UB。
操作数的计算是未排序的:vs 。UB 根据发生未排序副作用的多个允许顺序排序。j = ++j
=
j = ...
j + ((++j) & m)
++j
评论
m
j = (j | m) + 1 & ~m;
| m
+ 1
& ~m
m
j
+ m
| m
j = j + (m+1) & ~m
m+1
- ~m
j = j - ~m & ~m
~m
for( int x = 0; x < t; x += 1 + !!(x+1&m)*m )
...任何有一定经验的人都可以理解,同时不需要极端的心理体操......:-)