指针和后增操作的有趣事情

5

这个C/C++语句在理论上有什么问题(如果有的话):

*memory++ = BIT_MASK & *memory;

其中BIT_MASK是一个任意的按位AND掩码,而memory则是指针。

意图是读取内存位置,将值与掩码进行AND运算,将结果存储在原始位置,最后将指针增加到下一个内存位置。

2个回答

15
你正在调用未定义行为,因为你在单个语句中引用了memory两次(一次读取,一次写入),没有中间的序列点,并且语言标准没有指定增量将何时发生。(你可以多次读取同一内存;问题出现在尝试将一些写入与读取混合的情况下,如你的例子。)
你可以使用:
*memory++ &= BIT_MASK;

为了达到你想要的效果而不产生未定义行为,在C标准(ISO/IEC 9899:1999,也称为C99)中,第6.5节“表达式”中的第2段规定:

 

在前一个和下一个序列点之间,对象通过求值表达式最多仅修改一次其存储值。此外,先前的值只能读取以确定要存储的值。70)

这是C标准中的主要来源。脚注如下:

 

该段使语句表达式变得未定义,例如

i = ++i + 1;
a[i++] = i;

同时允许

i = i + 1;
a[i] = i;
此外,“附录C(信息性)序列点”对所有这些进行了广泛讨论。
您会在C ++标准中找到类似的措辞,但我不确定它是否有“附录C”的类比。

3
或者,更好的方法是:*memory &= BIT_MASK; ++memory; - Benjamin Lindley
我从未听说过这种未定义行为。有没有什么地方可以让我了解更多? - user1040049
4
@Benjamin:美在观者的眼中;我更倾向于简化为“审美因人而异”。 - Jonathan Leffler
1
明确一点,问题不在于两次引用 *memory,而在于两次引用 memory - Oliver Charlesworth

6

由于您在同一语句中使用了memory++memory,因此这是未定义的行为。

这是因为C/C++没有明确指定++将在何时发生。它可以在评估*memory之前或之后发生。

以下是两种修复方法:

*memory = BIT_MASK & *memory;
memory++;

或者简单地说:
*memory++ &= BIT_MASK;

选择你的喜好。

好的,谢谢提供信息。我一直以为后置递增会在语句的其余部分完成之后发生。我很惊讶C/C++标准中没有明确规定。 - user1069620
1
@NTHOLDER:目前没有一种主流编程语言将后缀++定义为在语句之后执行。例如,Java和C#都会立即递增变量(但返回旧值)。一个例子是int i = 0; a[i++] = i;,它将在索引0处存储值1。 - fredoverflow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接