Namespaces
Variants

Value categories

From cppreference.net

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 的表达式被假定为不产生左值的对象表达式,其生成的值既无具体表示形式也不需存储空间。

注意:具有数组成员(可能是嵌套成员)的结构体/联合体右值实际上指定了一个具有 临时生存期 的对象。该对象可以通过左值表达式进行访问,这些表达式通过索引数组成员或通过对数组成员进行数组到指针转换获得的指针进行间接寻址形成。

(since C99)

函数指示符表达式

函数指示符(由 函数声明 引入的标识符)是函数类型的表达式。当在除取址运算符、 sizeof _Alignof 之外的任何上下文中使用时(后两个运算符应用于函数时会产生编译错误),函数指示符总是被转换为指向函数的非左值指针。请注意,函数调用运算符是为指向函数的指针定义的,而不是为函数指示符本身定义的。

参考文献

  • C23 标准 (ISO/IEC 9899:2024):
  • 6.3.2.1 左值、数组和函数指示符 (页: 48-49)
  • C17 标准 (ISO/IEC 9899:2018):
  • 6.3.2.1 左值、数组和函数指示符 (p: 40)
  • C11 标准 (ISO/IEC 9899:2011):
  • 6.3.2.1 左值、数组和函数指示符 (页: 54-55)
  • C99标准(ISO/IEC 9899:1999):
  • 6.3.2.1 左值、数组和函数指示符(第46页)
  • C89/C90 标准 (ISO/IEC 9899:1990):
  • 3.2.2.1 左值与函数指示符

参见

C++ 文档 关于 值类别