Member access operators
成员访问运算符允许访问其操作数的成员。
| 运算符 | 运算符名称 | 示例 | 描述 |
|---|---|---|---|
| [ ] | 数组下标 | a [ b ] | 访问数组 a 的第 b 个元素 |
| * | 指针解引用 | * a | 解引用指针 a 以访问其指向的对象或函数 |
| & | 取地址 | & a | 创建指向对象或函数 a 的指针 |
| . | 成员访问 | a. b | 访问 结构体 或 联合体 a 的成员 b |
| - > | 指针成员访问 | a - > b | 访问由 a 指向的 结构体 或 联合体 的成员 b |
目录 |
下标运算符
数组下标表达式的形式为
指针表达式
[
整数表达式
]
|
(1) | ||||||||
整数表达式
[
指针表达式
]
|
(2) | ||||||||
其中
| pointer-expression | - | 指向完整对象的 表达式 |
| integer-expression | - | 整数类型的 表达式 |
下标运算符表达式是一种 左值表达式 ,其类型为 pointer-expression 所指向对象的类型。
根据定义,下标运算符 E1 [ E2 ] 完全等同于 * ( ( E1 ) + ( E2 ) ) 。若 pointer-expression 是数组表达式,它会经历 左值到右值转换 并成为指向数组首元素的指针。
根据 指针与整数的加法运算 定义,运算结果将指向索引值等于 整数表达式 计算结果的数组元素(或者,如果 指针表达式 原本指向某个数组的第i个元素,则结果元素的索引值为i加上 整数表达式 的计算结果)
注意:有关多维数组的详细信息,请参阅 array 。
#include <stdio.h> int main(void) { int a[3] = {1,2,3}; printf("%d %d\n", a[2], // n == 3 2[a]); // 相同,n == 3 a[2] = 7; // 下标是左值 int n[2][3] = {{1,2,3},{4,5,6}}; int (*p)[3] = &n[1]; // n的元素是数组 printf("%d %d %d\n", (*p)[0], p[0][1], p[0][2]); // 通过p访问n[1][] int x = n[1][2]; // 对数组n[1]再次应用[] printf("%d\n", x); printf("%c %c\n", "abc"[2], 2["abc"]); // 字符串字面量也是数组 }
输出:
3 3 4 5 6 6 c c
解引用
解引用 或 间接寻址 表达式的形式为
*
指针表达式
|
|||||||||
其中
| pointer-expression | - | 任意指针类型的 表达式 |
如果 pointer-expression 是指向函数的指针,则解引用运算符的结果是表示该函数的函数指示符。
如果 pointer-expression 是指向对象的指针,则结果为指向该对象的 左值表达式 。
解引用空指针、指向生命周期外对象的指针(悬垂指针)、未对齐指针或具有不确定值的指针是未定义行为,除非通过对其结果应用取址运算符来使解引用操作无效,例如 & * E 。
输出:
*p = 1 *p = 7
取地址
取址表达式的形式为
&
函数
|
(1) | ||||||||
&
左值表达式
|
(2) | ||||||||
&
*
表达式
|
(3) | ||||||||
&
表达式
[
表达式
]
|
(4) | ||||||||
&
和
*
相互抵消,两者均不参与求值
&
和
[]
中隐含的
*
相互抵消,仅计算
[]
中隐含的加法。
其中
| lvalue-expression | - | 任意类型的 左值 表达式,该类型不能是 位域 且不能具有 register 存储类 |
取地址运算符生成其操作数的 非左值 地址,适用于初始化指向操作数类型的指针。若操作数为函数指示符 (1) ,则结果为指向函数的指针。若操作数为对象 (2) ,则结果为指向对象的指针。
如果操作数是解引用运算符,则不执行任何操作(因此可以对空指针应用 &*),但结果不是左值。
如果操作数是数组索引表达式,除了数组到指针的转换和加法运算外不会执行其他操作,因此对于大小为N的数组,&a[N]是有效的(获取尾后指针是允许的,解引用它是不允许的,但在此表达式中解引用操作被抵消)。
int f(char c) { return c;} int main(void) { int n = 1; int *p = &n; // 对象 n 的地址 int (*fp)(char) = &f; // 函数 f 的地址 int a[3] = {1,2,3}; int *beg=a, *end=&a[3]; // 等价于 end = a+3 }
成员访问
成员访问表达式具有以下形式
表达式
.
成员名称
|
|||||||||
其中
| expression | - | 一个 结构体 或 联合体 类型的表达式 |
| member-name | - | 用于命名由 expression 指定的结构体或联合体成员的 标识符 |
成员访问表达式用于指定其左操作数所指向的 结构体 或 联合体 中的命名成员。其 值类别 与左操作数保持一致。
如果左操作数是 const 或 volatile 限定的,则结果也具有相同限定。如果左操作数是 atomic 的,则行为未定义。
注意:除了命名结构体或联合体类型对象的标识符外,以下表达式可能具有结构体或联合体类型: 赋值 、 函数调用 、 逗号运算符 、 条件运算符 以及 复合字面量 。
#include <stdio.h> struct s {int x;}; struct s f(void) { return (struct s){1}; } int main(void) { struct s s; s.x = 1; // 正确:修改s的成员 int n = f().x; // f()是struct s类型的表达式 // f().x = 1; // 错误:该成员访问表达式不是左值 const struct s sc; // sc.x = 3; // 错误:sc.x是常量,不可被赋值 union { int x; double d; } u = {1}; u.d = 0.1; // 修改联合体的活跃成员 }
通过指针进行成员访问
成员访问表达式具有以下形式
表达式
->
成员名称
|
|||||||||
其中
| expression | - | 指向 结构体 或 联合体 的 指针 类型表达式 |
| member-name | - | 命名由 expression 指向的结构体或联合体成员的 标识符 |
通过指针的成员访问表达式指定由其左操作数指向的 结构体 或 联合体 类型的命名成员。其值类别始终为 左值
如果左操作数所指向的类型具有 const 或 volatile 限定符,则结果也会带有相应限定符。如果左操作数所指向的类型为 atomic ,则行为未定义。
#include <stdio.h> struct s {int x;}; int main(void) { struct s s={1}, *p = &s; p->x = 7; // 通过指针修改 s.x 的值 printf("%d\n", p->x); // 输出 7 }
缺陷报告
下列行为变更缺陷报告被追溯应用于先前发布的C语言标准。
| 缺陷报告 | 适用范围 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| DR 076 | C89 |
不必要的间接寻址无法通过
&
运算符取消
|
改为可取消 |
参考文献
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.5.2.1 数组下标 (p: 57-58)
-
- 6.5.2.3 结构与联合成员 (p: 58-59)
-
- 6.5.3.2 地址与间接运算符 (p: 59-61)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.5.2.1 数组下标 (p: 80)
-
- 6.5.2.3 结构与联合成员 (p: 82-84)
-
- 6.5.3.2 地址与间接运算符 (p: 88-89)
- C99标准(ISO/IEC 9899:1999):
-
- 6.5.2.1 数组下标(第70页)
-
- 6.5.2.3 结构与联合成员(第72-74页)
-
- 6.5.3.2 取址与间接运算符(第78-79页)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 3.3.2.1 数组下标
-
- 3.3.2.3 结构体和联合体成员
-
- 3.3.3.2 取址与间接运算符
参见
| 常用运算符 | ||||||
|---|---|---|---|---|---|---|
| 赋值 |
自增
自减 |
算术 | 逻辑 | 比较 |
成员
访问 |
其他 |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
b
]
|
a
(
...
)
|
|
C++ 文档
关于
成员访问运算符
|