请帮我理解以下表达式:
(在一本书中看到)
*((int *)marks + i++) = i+1
大量的增量和符号取消引用令人困惑!
我希望这本书有一个不好的例子,因为它的行为是不确定的。
(int *)marks
将marks
(无论那是什么)解释为指向int
的指针,那么我们得到了i++
添加的结果。此指针被取消引用,i+1
分配给相应的对象。
此表达式没有定义的行为,因为它读取和修改两个不同的子表达式i
,这两个子表达式未先于另一个排序。
烧掉书。
由于没有序列点,语句的行为是未定义的。一个更简单理解的情况是i++ = i
,它也是未定义的。
注意:正如其他人所说,i++
表达式是未定义的行为。在 g++ 中,i++
操作将按如下方式执行:
// Pointer to some data.
void* marks = ???;
// Typecast to an integer pointer
int* marksIntPointer = (int*)marks;
// Move the position in memory. I am assuming that 'marks' is an array.
int* marksIntPointerOffset = marksIntPointer + i;
// Undefined behaviour, could take place here or before, depending on the compiler (as others have said).
i++;
// Set the value of the desired memory.
*marksIntPointerOffset = i+1;
提到的表达式会产生未定义的行为。
C99 6.5 §2指出:
1) 在上一个和下一个序列点之间,对象应具有其存储值 通过表达式的计算最多修改一次。
2)此外,应仅读取先前的值以确定要存储的值。
1)在单个表达式中存储和修改变量的值非常简单:
i = ++i;
i++ = i;
++i = 7;
以及在单个表达式中多次修改同一变量的值:
j = ++i + ++i;
2)仅读取以确定要存储的值可能有点棘手。这意味着即使以下(就像前面的示例一样)也会调用未定义的行为:
j = (i + 1) + i++;
a[i++] = i;
*(ptr + i++) = i;
以及:
*((int *)marks + i++) = i+1
您可以查看:未定义的行为和序列点以及:)
正如其他人所提到的,行为是未定义的;通过摆脱++
运算符,以下代码将被很好地定义(但仍然像罪恶一样丑陋):
*((int *)marks + i) = i+1
以下是它的分解方式:
marks -- take the expression marks
(int *)marks -- cast it as a pointer to int
(int *)marks + i -- offset i integer elements from that address
*((int *)marks + i) -- deference the result to get an array element
*((int *)marks + i) = i+1 -- assign the result of i+1 to that element
这实质上是将marks
视为int
数组,并将i+1
的结果分配给第i
个元素:
int *mp = (int *) marks;
mp[i] = i+1
最初的表达试图做
mp[i++] = i+1
调用未定义的行为。 由于没有指定i++
和i+1
的评估顺序;编译器可以自由地以任何感觉的顺序评估它们。 由于i++
有一个副作用(更新i
的值),这意味着您将根据平台,优化设置甚至周围的代码获得不同的结果。 语言标准显式地未定义行为,以便编译器不需要以任何特定方式处理此代码。 你会得到一个结果,但不能保证它在编译器之间(甚至从运行到运行)是一致的。
(int *)marks
将标记转换为整数指针
+ i++
在标记处添加 i(标记指向的地址),然后将 i 递增 1
*(...) = i+1
将指针指向的单元格的 VALUE 设置为 i+1(请注意,i 之前已经递增,因此在指令开始时它将大于 2。
我希望这是一个示例,例如您不应该如何编写代码:)
让我们看看。
*((整数 *)标记 + i++) = i+1
-标记被强制转换为指向
int-i 的指针递增 1-指针 (
int *)标记使用指针算法按值 (i+1) 前进
- 此指针现在被取消引用,所以...
-...它指向的内存位置现在以 i+1 的值写入