成员访问运算符
成员访问运算符允许访问其运算数的成员。
运算符 | 运算符名 | 示例 | 描述 |
---|---|---|---|
[] | 数组下标 | a[b] | 访问数组 a 的第 b 个元素 |
* | 指针解引用 | *a | 解引用指针 a 以访问其所指向的对象或函数 |
& | 取址 | &a | 创建指向对象或函数 a 的指针 |
. | 成员访问 | a.b | 访问结构体或联合体 a 的成员 b |
-> | 通过指针的成员访问 | a->b | 访问 a 所指向的结构体或联合体 的成员 b |
下标
数组下标运算符拥有形式
指针表达式 [ 整数表达式 ]
|
(1) | ||||||||
整数表达式 [ 指针表达式 ]
|
(2) | ||||||||
其中
指针表达式 | - | 指向完整对象指针类型的表达式 |
整数表达式 | - | 整数类型表达式 |
下标运算符为左值表达式,其类型为 指针表达式 所指向的对象的类型。
按照定义,下标运算符 E1[E2] 准确等同于 *((E1)+(E2)) 。若 指针表达式 为数组表达式,则它经历左值到右值转换并成为指向数组首元素的指针。
由于指针与整数间加法的定义,结果是下标等于 整数表达式 结果的数组元素(或若 指针表达式 指向某数组的第 i 个元素,则结果的下标为 i 加上 整数表达式 的结果)。
注意:多维数组上的细节见数组。
输出:
3 3 c c
解引用
解引用或间接表达式拥有形式
* 指针表达式
|
|||||||||
其中
指针表达式 | - | 任何指针类型的表达式 |
若 指针表达式 为指向函数指针,则解引用运算符的结果为该函数的函数指代器。
若 指针表达式 为指向对象指针,则结果为指代被指向对象的左值表达式。
解引用空指针、指向在生存期外的对象的指针(悬垂指针)、错误对齐的指针或拥有不确定值的指针是未定义行为,除非如在 &*E 中一般,通过应用取址运算符到解引用运算符的结果,将它取消。
输出:
*p = 1 *p = 7
取址
取址运算符拥有形式
& 函数
|
(1) | ||||||||
& 左值表达式
|
(2) | ||||||||
& * 表达式
|
(3) | ||||||||
& 表达式 [ 表达式 ]
|
(4) | ||||||||
&
与 *
彼此抵消,均不求值取址运算符产生其运算数的非左值地址,适于初始化指向运算数类型的指针。若运算数为函数指代器 ((1)) ,则结果为指向函数指针。若运算数为对象 ((2)) ,则结果为指向对象指针。
若运算数为解引用运算符,则不进行动作(故可以应用 &* 到空指针),除了结果是非左值。
若运算数是数组下标表达式,则不进行数组到指针转换和加法外的动作,故 &a[N] 对大小为 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 = n+3 }
成员访问
成员访问运算符拥有形式
表达式 . 成员名
|
|||||||||
其中
表达式 | - | 结构体或联合体类型表达式 |
成员名 | - | 指名 表达式 所指带的结构体或联合体的成员的标识符 |
成员访问表达式指代其左运算数所指代的 struct 或 union 的具名成员。它拥有与其左运算数相同的值类别。
若左运算为 const 或 volatile 限定,则结果亦有限定。若左运算数为原子对象,则行为未定义。
注意:除了指名结构体或联合体的标识符,下列表达式亦可拥有结构体或联合体类型:赋值、函数调用、逗号运算符、条件运算符和复合字面量。
#include <stdio.h> struct s {int x;}; struct s f(void) { return (struct s){1}; } int main(void) { struct s s; s.x = 1; // OK :更改 s 的成员 int n = f().x; // f() 为 struct s 类型表达式 // f().x = 1; // 错误:此成员访问表达式非左值 const struct s sc; // sc.x = 3; // 错误: sc.x 为 const ,不能被赋值 union { int x; double d; } u = {1}; u.d = 0.1; // 更改 union 的活跃成员 }
通过指针的成员访问
成员访问表达式拥有形式
表达式 -> 成员名
|
|||||||||
其中
表达式 | - | 指向结构体或联合体的指针类型表达式 |
成员名 | - | 指名 表达式 所指向的结构体或联合体的成员的标识符 |
通过指针的成员访问表达式指代其左运算数所指向的 struct 或 union 类型的具名成员。其值类别始终为左值。
若左运算数所指向的类型有 const 或 volatile 限定,则结果亦有限定。若左运算数所指向的类型为原子类型,则行为未定义。
#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 Array subscripting (第 57-58 页)
- 6.5.2.3 Structure and union members (第 58-59 页)
- 6.5.3.2 Address and indirection operators (第 59-61 页)
- C11 标准(ISO/IEC 9899:2011):
- 6.5.2.1 Array subscripting (第 80 页)
- 6.5.2.3 Structure and union members (第 82-84 页)
- 6.5.3.2 Address and indirection operators (第 88-89 页)
- C99 标准(ISO/IEC 9899:1999):
- 6.5.2.1 Array subscripting (第 70 页)
- 6.5.2.3 Structure and union members (第 72-74 页)
- 6.5.3.2 Address and indirection operators (第 78-79 页)
- C89/C90 标准(ISO/IEC 9899:1990):
- 3.3.2.1 Array subscripting
- 3.3.2.3 Structure and union members
- 3.3.3.2 Address and indirection operators
参阅
常用运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 自增 自减 |
算术 | 逻辑 | 比较 | 成员 访问 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[b] |
a(...) |