10

I have just started to learn C, and I get that

*a = *b;
a++;
b++;

and

*a++ = *b++

are equivalent, but is that what's actually happening when the line

*a++ = *b++

is called? Can someone clarify how the compiler is interpreting the second line? I know about right-to-left precedence and such, but can someone precisely write the steps the compiler uses to interpret this line of code?

4

3 回答 3

14

你说你相信:

*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# 方式执行此操作,但不是必需的。

于 2013-07-29T22:07:30.217 回答
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;
于 2013-07-29T14:40:20.270 回答
3

计算表达式和应用副作用的确切顺序未指定;所有可以保证的是*b++b当前指向的值)的结果被分配给*a++a当前指向的值)的结果,并且两个指针都是前进的。操作的确切顺序会有所不同。

如果您想知道您的平台如何处理它,您可以查看生成的机器代码,但请注意,它仍可能因编译器设置或周围代码而异。

于 2013-07-29T16:17:02.210 回答