嗯,我并不是真的非常需要这个答案,我只是好奇。
*ptr++ = a
之类的表达式完全有效,因为我们对两个对象ptr
和*ptr
进行操作,但如果我写*ptr++ = *ptr + a
它是否仍然有效?
例如,请考虑以下代码段:
int main(void){
int a[] = {5,7,8,9,2};
int* p =a;
*p++ = 76; /*altering the first element */
*p++ = *p + 32; /*altering the second element */
p = a;
int i;
for(i = 0;i<5; i++)
printf("%d ",*p++);
return 0;
}
我认为表达式*p++ = *p + 32;
没有什么可担心的,但我不确定所涉及的序列点。
答案 0 :(得分:12)
*ptr++ = *ptr + a
的结果未定义。等号不是序列点,因此在该语句中再次使用ptr
的值会导致未定义的行为。在评估RHS表达式之前,如果ptr
递增,请考虑结果,并将其与 RHS表达式后ptr
递增的情况进行比较。
注意:这并不是说表达式的结果来自这两种情况中的任何一种。未定义未定义。
基本上,你只能指望两件事:在最后一个序列点和下一个序列点之间的某个时间评估后增量,并且表达式ptr++
返回值ptr
之前它会递增。所以*ptr++ = a
很好,因为您可以依靠ptr++
的结果。
答案 1 :(得分:8)
首先让我们假设'p'是指针类型 否则,所有操作都只是函数调用的语法糖。
让我们将声明分成几部分。
int* p = a;
*p++ = *p + 32;
<< Sequence Point >>
// Part 1: p++
// Note the definition of post increment in the standard is (5.2.6)
// The result of the expression p++ is the value of 'p' while the value of the
// objects represented by 'p' is incremented. This can be represented in pseudo code as:
(A) int* p1 = p;
(B) p = p + 1;
// Part 2: *p (On the result of Part 1) (On *p++)
(C) int& p2 = *p1; // Note the use of p1;
// Part 3: *p (On *p + 32)
// Note: There is no linkage between this use of 'p' and the 'p' in Part 1&2
(D) int& p3 = *p;
// Part 4: *p + 32;
(E) int p5 = p3 + 32; // Note the use of p3;
// Part 5: Assignment.
(F) p2 = p5;
<< Sequence Point >>
Ordering that must be preserved:
(A) Before (B)
(A) Before (C)
(D) Before (E)
(C) Before (F)
(E) Before (F)
鉴于上述限制:
编译器可以通过多种方式重新排序这些指令,
但要注意的要点是(B)可以发生在(B)的唯一约束是它发生在(A)之后的任何地方。因此,(D)中定义的p3的值可以是两个不同值中的一个,具体取决于确切位置(B)。
由于此处无法定义p3的值 结果语句具有未定义的行为。
答案 2 :(得分:1)
就C而言,*ptr++ = *ptr + 32
将根据6.5.2未定义:“如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值,行为未定义。“您正在修改并尝试在另一个计算中使用ptr
的值而没有插入序列点。
答案 3 :(得分:0)
如果您要问的话,那就不一样了。 它会编译但是......
这是合法的 - 如果PTR指向一个数组,而不是指向该数组的最后一个单元格, 因此递增PTR将指向数组中的下一个对象。
* ptr ++ = * ptr + 2 与* ptr = * ptr + 2,ptr ++
相同答案 4 :(得分:0)
我认为这是未定义的。但是我不确定。
但是,有点更广泛的风格观点:如果一个表达式让你开始怀疑它是否未定义,也许这是一个标志,你的代码不清楚它的意图,需要更少模糊,并且更少的非明显依赖于评估顺序。
我曾经认为C非常酷,因为你可以编写很多非常简短的语句来做很多疯狂的事情。我不再那么想了。 : - )