我刚开始学C,我明白了
*a = *b;
a++;
b++;
和
*a++ = *b++
是等价的,但是当行
*a++ = *b++
叫什么?有人可以澄清编译器是如何解释第二行的吗?我知道从右到左的优先顺序等等,但是有人能准确地写出编译器用来解释这行代码的步骤吗?
最佳答案
你说你相信:
*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
按照您的建议这样做也是合法的:首先进行分配,然后按从左到右的顺序递增。并且先进行赋值,然后按从右到左的顺序递增也是合法的。
C 编译器被赋予了广泛的自由来生成代码,但是它喜欢这种表达式。确保这一点在您的脑海中非常清楚,因为大多数人都犯了这个错误:只是因为 ++
出现在变量之后并不意味着增量发生得晚。增量可以在编译器喜欢的时候尽早发生,只要编译器确保使用原始值。
这是 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 编译器,但不是要求。
关于c - 递增指针,精确序列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17926794/