我刚开始学习C,我明白了

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


*a++ = *b++

是等价的,但这是生产线实际发生的情况
*a++ = *b++

叫做?有人可以澄清编译器如何解释第二行吗?我知道从右到左的优先顺序,但是有人可以准确地编写编译器用来解释这一行代码的步骤吗?

最佳答案

您说您相信:

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

相当于
*a++ = *b++;

但这是错误的,因此您有错误的信念。让我们纠正您的错误信念。

在第一种情况下,必须发生以下情况:
  • VAR:必须对*a求值才能产生一个变量,将其称为var
  • VAL:必须对*b求值才能产生一个值,将其称为val
  • ASSIGN:必须将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/

    10-10 13:50
    查看更多