值类别

< c‎ | language

C中每个表达式(带有参数的运算符、函数调用、常量、变量名等)以两个独立属性刻画:类型值类别

每个表达式属于三个值类别之一:左值、非左值对象(右值)以及函数指代器。

左值表达式

左值表达式是任何类型异于 void对象类型,且隐含地指代一个对象的表达式(当左值在求值时不实际指代一个对象时,行为未定义)。换言之,左值表达式求值得到对象标识。此值类别的名称(“左值”)是历史性的,并反映了 CPL 中,左值表达式作为赋值运算符的左运算数。

左值表达式可用于下列左值语境

若将左值表达式用于异于 sizeof_Alignof 或上述的运算符,则任何完整类型的非数组左值会经历左值转换,这模仿的是从对象位置到其值的内存加载。同样地,在用于异于 sizeof_Alignof 、取址运算符或从字符串字面量初始化数组的语境时,数组左值会经历数组到指针转换

const/volatile/restrict 限定符和原子类型的语义仅应用于左值(左值转换将剥除限定符并移除原子属性)。

下列表达式是左值:

  • 标识符,含函数参数,只要他们被声明为指代对象(而非函数)
  • 字符串字面量
  • (C99) 复合字面量
  • 括号表达式,若其无括号版本是左值
  • 成员访问(点)运算符的结果,若其左参数是左值
  • 由指针访问成员( -> )运算符的结果
  • 间接使用运算符(一元 * )作用域指向对象指针
  • 下标运算符的结果( []

可修改左值表达式

一个可修改左值是任何完整类型、非数组、且非 const 限定的左值表达式,而且若它是结构体/联合体,则递归地没有任何成员为 const 限定。

只有可修改左值表达式可用作自增减运算符的参数,赋值和复合赋值运算符的左参数。

非左值对象表达式

通称为右值,非左值表达式是不指代对象的对象类型表达式,而是没有对象身份或存储位置的值。不能对非左值表达式取址。

下列表达式是非左值对象表达式:

  • 整数、字符、浮点数常量
  • 所有不返回左值的运算符,包括
  • 任何函数表达式
  • 任何转换类型表达式(注意看起来相似的复合字面量是左值)
  • 作用于非左值结构体/联合体的的成员访问(点)运算符,f().x(x,s1).a(s1=s2).m
  • 所有算术、关系、逻辑及位运算符
  • 自增和自减运算符(注意:前置形式在 C++ 中是左值)
  • 赋值及复合赋值运算符(注意:它们在 C++ 中是左值)
  • 条件运算符(注意:在 C++ 中可能是左值)
  • 逗号运算符(注意:在 C++ 中可能是左值)
  • 取址运算符,即使它被用一元 * 运算符的结果中和

作为特殊情况, void 类型表达式被假设成非左值对象表达式,得出一个不存在表示且不要求存储的值。

注意,拥有一个数组类型成员(可以是嵌套的)的结构体/联合体右值实际上指代一个拥有临时生存期的对象。此对象可通过由索引数组元素组成的左值表达式,或解引用该数组的数组到指针转换所得的指针访问。

函数指代器表达式

函数指代器(由函数声明引入的标识符)是函数类型的表达式。当在异于取址运算符、 sizeof_Alignof (后两者在作用于函数时生成编译错误)的语境中时,函数指代器始终转换成指向函数的非左值指针。注意函数调用运算符是对指向函数指针,而非对函数指代器自身定义的。

引用

  • C11 standard (ISO/IEC 9899:2011):
  • 6.3.2.1 Lvalues, arrays, and function designators (p: 54-55)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.3.2.1 Lvalues, arrays, and function designators (p: 46)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.2.2.1 Lvalues and function designators

参阅