值类别
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
(最后两个在应用于函数时会生成编译错误)以外的任何上下文中使用时,函数标识符始终转换为指向函数的非左值指针。请注意,函数调用运算符是为指向函数的指针定义的,而不是为函数标识符本身定义的。
[编辑] 引用
- C17 标准 (ISO/IEC 9899:2018)
- 6.3.2.1 左值、数组和函数标识符 (p: 40)
- C11 标准 (ISO/IEC 9899:2011)
- 6.3.2.1 左值、数组和函数标识符 (p: 54-55)
- C99 标准 (ISO/IEC 9899:1999)
- 6.3.2.1 左值、数组和函数标识符 (p: 46)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.2.2.1 左值和函数标识符
[编辑] 另请参阅
C++ 文档 用于 值类别
|