我刚刚开始学习C,我得到了
*a = *b;
a++;
b++;
和
*a++ = *b++
是等价的,但是当行
时实际发生的是什么*a++ = *b++
被称为?有人可以澄清编译器如何解释第二行吗?我知道从右到左的优先级等等,但有人可以准确地编写编译器用来解释这行代码的步骤吗?
答案 0 :(得分:13)
你说你相信:
*a = *b; a++; b++;
相当于
*a++ = *b++;
但这是假的,所以你有一个错误的信念。让我们纠正你的错误信念。
在第一种情况下,必须发生以下事情:
*a
必须经过评估才能生成变量,称之为var
*b
必须经过评估才能生成一个值,称之为val
val
必须分配到var
。a
必须递增。b
必须递增。编译器如何订购这些限制有哪些限制?
这里的规则是一个语句的所有副作用必须在下一个语句开始之前完成。因此有两个合法的排序。 VAR VAL ASSIGN INCA INCB,或VAL VAR ASSIGN INCA INCB。
现在让我们考虑第二种情况。
*a++ = *b++;
我们有相同的五个操作,但它们的排序约束完全不同,因为它们都在同一个语句中,所以关于语句的规则不适用。现在约束是:
a
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#中的相同代码将 required 生成为:
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#方式执行,但不是必需。
答案 1 :(得分:4)
1)
*a = *b;
a++;
b++;
相当于
*a = *b;
a = a+1;
b = b+1
2)
x = *a++
相当于
x = *a;
a = a+1;
和
*b++ = x
相当于
*b = x;
b = b+1;
所以
*a++ = *b++
相当于
*a = *b;
a = a+1;
b = b+1
3)
*(++a) = *(++b)
相当于
a = a+1;
b = b+1
*a = *b;
答案 2 :(得分:3)
评估表达式和应用副作用的确切顺序是未指定;所有保证的是,*b++
(b
当前指向的值)的结果被分配给*a++
的结果(a
当前指向的值),这两个指针都是先进的。确切的操作顺序会有所不同。
如果您想知道您的平台如何处理它,您可以查看生成的机器代码,但请注意它仍然可能因编译器设置或周围代码而异。