成员访问运算符允许访问其运算数的成员。
运算符 | 运算符名 | 示例 | 描述 |
---|---|---|---|
[] | 数组下标 | a[b] | 访问数组 a 的第 b 个元素 |
* | 指针解引用 | *a | 解引用指针 a 以访问其所指向的对象或函数 |
& | 取址 | &a | 创建指向对象或函数 a 的指针 |
. | 成员访问 | a.b | 访问结构体或联合体 a 的成员 b |
-> | 通过指针的成员访问 | a->b | 访问 a 所指向的结构体或联合体 的成员 b |
数组下标运算符拥有形式
pointer-expression [ integer-expression ] |
(1) | ||||||||
integer-expression [ pointer-expression ] |
(2) | ||||||||
其中
pointer-expression | - | 指向完整对象指针类型的表达式 |
integer-expression | - | 整数类型表达式 |
下标运算符为左值表达式,其类型为 pointer-expression 所指向的对象的类型。
按照定义,下标运算符 E1[E2] 准确等同于 *((E1)+(E2)) 。若 pointer-expression 为数组表达式,则它经历左值到右值转换并成为指向数组首元素的指针。
由于指针与整数间加法的定义,结果是下标等于 integer-expression 结果的数组元素(或若 pointer-expression指向某数组的第 i 个元素,则结果的下标为 i 加上 integer-expression 的结果)。
注意:多维数组上的细节见数组。
运行此代码
#include <stdio.h> int main(void) { int a[3] = {1,2,3}; printf("%d %d ", 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 的元素为数组 int x = n[1][2]; // 再次应用 [] 到数组 n[1] printf("%c %c ", "abc"[2], 2["abc"]); // 字符串字面量亦是数组 }
输出:
3 3 c c
解引用或间接表达式拥有形式
* pointer-expression |
|||||||||
其中
pointer-expression | - | an expression of any pointer type |
若 pointer-expression 为指向函数指针,则解引用运算符的结果为该函数的函数指代器。
若 pointer-expression 为指向对象指针,则结果为指代被指向对象的左值表达式。
解引用空指针、指向在生存期外的对象的指针(悬垂指针)、错误对齐的指针或拥有不确定值的指针是未定义行为,除非如在 &*E 中一般,通过应用取址运算符到解引用运算符的结果,将它置空。
运行此代码
#include <stdio.h> int main(void) { int n = 1; int* p = &n; printf("*p = %d ", *p); // *p 的值即为存储于 n 的值 *p = 7; // *p 是左值 printf("*p = %d ", *p); }
输出:
*p = 1 *p = 7
取址运算符拥有形式
& function |
(1) | ||||||||
& lvalue-expression |
(2) | ||||||||
& * expression |
(3) | ||||||||
& expression [ expression ] |
(4) | ||||||||
1) 函数地址
2) 对象地址
3) 特殊情况: & 与 * 彼此抵消,均不求值
4) 特殊情况: & 与 [] 中隐含的 * 抵消,只求值 [] 中隐含的加法
其中
lvalue-expression | - | 拥有任何非位域类型且无 register 存储类的左值表达式 |
取址运算符产生其运算数的非左值地址,适于初始化指向运算数类型的指针。若运算数为函数指代器 ((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 }
成员访问运算符拥有形式
expression . member-name |
|||||||||
其中
expression | - | 结构体或联合体类型表达式 |
member-name | - | 指名 expression 所指带的结构体或联合体的成员的标识符 |
成员访问表达式指代其左运算数所指代的 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 的活跃成员 }
成员访问表达式拥有形式
expression -> member-name |
|||||||||
其中
expression | - | 指向结构体或联合体的指针类型表达式 |
member-name | - | 指名 expression 所指向的结构体或联合体的成员的标识符 |
通过指针的成员访问表达式指代其左运算数所指向的 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 ", p->x); // 打印 7 }
常用运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 自增 自减 |
算术 | 逻辑 | 比较 | 成员 访问 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[b] |
a(...) |