【问题标题】:Does every expression in C make a copy of the variables?C 中的每个表达式都会复制变量吗?
【发布时间】:2020-05-25 06:54:34
【问题描述】:

当谈到 C 中的表达式时,我试图理解 R 值和 L 值,并且我知道许多表达式不是有效的 L 值,因为最终结果的地址是未知的。这是因为在许多情况下,表达式中使用了变量的副本,因此副本的地址是未知的。例如,

    char ch = 'a';
    char *p = &ch;
    p + 1; //This expression can be used as an R-value, but not an L-value

我相信表达式中发生的事情(如果我错了,请纠正我)是创建了 p 的副本,将 1 添加到该副本中,以便 p+1 指向 ch 之后的字符,但是这个新的 p+1 指针的地址是未知的,所以它不能用作 L 值。

复制变量并在表达式中使用副本的行为是否发生在 C 中的所有表达式中?例如,如果我有

    int a = 100;
    int b = 25;
    a - b;

是创建了变量a的副本(并存储在未知位置)和创建了变量b的副本(b的副本也存储在未知位置),副本中的数据用于减去然后结果存储在另一个未知位置,还是从原始变量中提取数据,减去,然后将结果存储在未知位置?

【问题讨论】:

  • 在你的最后一段中,两个选项都是一样的

标签: c rvalue lvalue


【解决方案1】:

C 中的每个表达式都会复制变量吗?

没关系。

编译器有优化。只要副作用相同,编译器就可以对代码做任何事情。在您的示例中,p + 1;a - b; 什么都不做,它们可以被编译器优化,所以什么也不会发生。

使用编译器生成的代码在实际机器上发生的事情是高度机器特定的。编译器可以先将变量的值加载到一些寄存器,然后对这些寄存器执行计算——或者不执行计算,可以在存储值时执行计算,或者甚至不为变量保留任何内存。

我知道很多表达式不是有效的 L 值,因为末尾的地址 结果未知

这可能是您可以证明这一点的根本原因,但规则更直接。 C 标准仅列出运算符并表示the result of + is a valuethe result of * is a lvalue。没有“通用”规则。

这种复制变量并在表达式中使用副本的行为是否发生在 C 中的所有表达式中?

C 标准不谈论“变量”或“变量的副本”,C 标准谈​​论“值”。表达式具有定义的值、定义的最终结果。 C 标准并不真正关心编译器将如何达到该值。所以实际的行为可以是任何东西。如果一个特定的编译器决定复制变量或决定不 - 对他有好处,只要最终结果是正确的。

lvalue 可以在= 运算符的左侧。不一定是变量,例如((char*)0x01)[1] 是一个左值,尽管它是某些运算符的结果。 rvalue(或只是 value)是您无法分配的值。

【讨论】:

    【解决方案2】:

    此类计算的实现不是标准的,因此它们在不同的编译器中可能会有所不同。要理解的重要一点是,R 值代表临时值。它可以是一个寄存器或一些分配的内存。

    在 CPU 中对寄存器进行计算。因此,编译器会将变量的值移动到寄存器中,然后计算减法。 这是这样一个计算的集合:

     659:   c7 45 f8 64 00 00 00    movl   $0x64,-0x8(%rbp)
     660:   c7 45 fc 19 00 00 00    movl   $0x19,-0x4(%rbp)
     667:   8b 45 f8                mov    -0x8(%rbp),%eax
     66a:   2b 45 fc                sub    -0x4(%rbp),%eax
    

    您可以看到值 100(十六进制的 64)和 25(十六进制的 19)保存在堆栈中相对于基指针的地址(分别为 -0x8、-0x4)。 比来自 -0x8 的值移动到 eax 寄存器中,来自 -0x4 的值从寄存器中的值中减去并存储在寄存器本身中。

    如您所见,结果将在寄存器中结束,因此它在内存中没有地址。

    【讨论】:

      【解决方案3】:

      根据 C11 标准 (ISO-IEC-9899-2011)--6.3.2.1 左值、数组和函数指示符:

      除了 sizeof 运算符、一元 & 运算符、++ 的操作数 运算符、-- 运算符或 . 的左操作数。运算符或赋值运算符, 不具有数组类型的左值被转换为存储在指定的值 对象(不再是左值);这称为左值转换。

      【讨论】:

        猜你喜欢
        • 2022-01-02
        • 2020-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-21
        • 2014-02-20
        • 1970-01-01
        • 2013-09-25
        相关资源
        最近更新 更多