两次访问相同的内存位置,UB与否?

Lun*_*din 5 c undefined-behavior language-lawyer

这个帖子中,评分最高的答案获得了大量的票数甚至是赏金.它提出了以下算法:

void RemoveSpaces(char* source)
{
  char* i = source;
  char* j = source;
  while(*j != 0)
  {
    *i = *j++;         // UB?
    if(*i != ' ')
      i++;
  }
  *i = 0;
}
Run Code Online (Sandbox Code Playgroud)

我的膝盖反射是这个代码调用未定义的行为,因为ij指向相同的内存位置,然后一个表达式*i = *j++;然后访问相同的变量两次,用于其他目的,而不是确定要存储的内容,两者之间没有序列点.尽管它们是两个不同的变量,但它们最初指向相同的存储位置.

但是我不确定,因为我不太清楚同一个内存位置的两个非顺序访问如何在实践中造成任何伤害.

我是否正确地指出这是未定义的行为?如果是这样,是否有任何关于如何依赖这种UB可能导致有害行为的例子?


编辑

将此标记为UB的C标准的相关部分是:

C99 6.5

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值.

C11 6.5

如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.

在两个版本的标准中,文本的实际含义应该相同,但我相信C99文本更容易阅读和理解.

gna*_*729 6

在两种情况下,两次访问同一对象而没有插入序列点是未定义的行为:

  1. 如果修改同一个对象两次.例如

    int x = (*p = 1, 1) + (*p = 2, 100);
    
    Run Code Online (Sandbox Code Playgroud)

    显然你不会知道*p之后是否为1或2,但C标准中的措辞表明它是未定义的行为,即使你写的

    int x = (*p = 1, 1) + (*p = 1, 100);
    
    Run Code Online (Sandbox Code Playgroud)

    因此,存储两次相同的值并不能节省您的时间.

  2. 如果修改了对象,也可以在不使用读取值的情况下读取它来确定对象的新值.这意味着

    *p = *p + 1; 
    
    Run Code Online (Sandbox Code Playgroud)

很好,因为你读*p,你修改*p,但你读*p,以确定存储的值*.


For*_*Bru 0

我认为这不会导致UB。在我看来,这就像说一样好

int k=0;
k=k; //useless but does no harm
Run Code Online (Sandbox Code Playgroud)

从内存中读取数据然后将其写入同一位置不会造成任何损害。