你说你相信:
*a = *b; a++; b++;
相当于
*a++ = *b++;
但那是错误的,所以你有一个错误的信念。让我们纠正你的错误信念。
在第一种情况下,必须发生以下事情:
- VAR:
*a
必须评估以产生变量,调用它var
- VAL:
*b
必须被评估以产生一个值,调用它val
- 分配:
val
必须分配给var
。
- INCA:
a
必须递增。
- INCB:
b
必须递增。
编译器如何排序这些限制是什么?
- VAR 和 VAL 必须在 ASSIGN 之前发生。
- ASSIGN 必须在 INCA 之前发生。
- INCA 必须在 INCB 之前发生。
这里的规则是一个语句的所有副作用必须在下一个语句开始之前完成。所以有两个法律命令。VAR VAL ASSIGN INCA INCB,或 VAL VAR ASSIGN INCA INCB。
现在让我们考虑第二种情况。
*a++ = *b++;
我们有相同的五个操作,但它们的排序约束完全不同,因为它们都在同一个语句中,所以关于语句的规则不适用。现在的约束是:
- VAR 和 VAL 必须在 ASSIGN 之前发生。
- VAR的评估必须使用原始值
a
- VAL的评估必须使用原始值
b
请注意,我没有说增量必须在之后发生。相反,我说必须使用原始值。 只要使用原始值,增量就可以随时发生。
因此,例如,将其生成为完全合法的
var = a;
a = a + 1; // increment a before assign
*var = *b;
b = b + 1; // increment b after assign
这样做也是合法的:
val = *b;
b = b + 1; // increment b before assign
*a = val;
a = a + 1; // increment a after assign
按照您的建议进行操作也是合法的:首先进行分配,然后以从左到右的顺序递增。首先进行分配也是合法的,然后以从右到左的顺序递增。
AC 编译器有很大的自由度来生成代码,但它喜欢这种表达方式。确保这在您的脑海中非常清楚,因为大多数人都弄错了:仅仅因为++
变量出现在变量之后并不意味着增量发生得晚。只要编译器确保使用原始值,增量就可以在编译器喜欢的时候发生。
这是 C 和 C++ 的规则。在 C# 中,语言规范要求赋值左侧的副作用发生在赋值右侧的副作用之前,并且两者都发生在赋值的副作用之前。需要将 C# 中的相同代码生成为:
var_a = a;
a = a + 1;
// must pointer check var_a here
var_b = b;
b = b + 1;
val = *var_b; // pointer checks var_b
*var_a = val;
“指针检查”是 C# 要求运行时验证是否var_a
为有效指针的点;换句话说,这*var_a
实际上是一个变量。如果不是,那么它必须在评估之前 抛出异常。b
同样,允许C 编译器以 C# 方式执行此操作,但不是必需的。