命名空间
变体
操作

成员访问运算符

来自 cppreference.com
< c‎ | 语言

成员访问运算符允许访问其操作数的成员。

运算符 运算符名称 示例 描述
[] 数组下标 a[b] 访问数组 a 的第 b 个元素
* 指针解引用 *a 解引用指针 a 以访问它所指向的对象或函数
& 取地址 &a 创建一个指向对象或函数 a 的指针
. 成员访问 a.b 访问结构体或联合体 a 的成员 b
-> 通过指针访问成员 a->b 访问由 a 指向的结构体或联合体的成员 b

内容

[编辑] 下标

数组下标表达式具有以下形式

指针表达式 [ 整数表达式 ] (1)
整数表达式 [ 指针表达式 ] (2)

其中

指针表达式 - 类型为指向完整对象的指针的 表达式
整数表达式 - 类型为整数的 表达式

下标运算符表达式是一个 左值表达式,其类型为 指针表达式 所指向的对象的类型。

根据定义,下标运算符 E1[E2]*((E1)+(E2)) 完全相同。如果 指针表达式 是一个数组表达式,它将进行 左值到右值转换 并成为指向数组第一个元素的指针。

由于 指针和整数之间的加法 的定义,结果是数组中索引等于 整数表达式 结果的元素(或者,如果 指针表达式 指向某个数组的第 i 个元素,则结果的索引为 i 加上 整数表达式 的结果)。

注意:有关多维数组的详细信息,请参阅 数组

#include <stdio.h>
int main(void)
{
    int a[3] = {1,2,3};
    printf("%d %d\n", a[2],  // n == 3
                      2[a]); // same, n == 3
    a[2] = 7; // subscripts are lvalues
 
    int n[2][3] = {{1,2,3},{4,5,6}};
    int (*p)[3] = &n[1]; // elements of n are arrays
    printf("%d %d %d\n", (*p)[0], p[0][1], p[0][2]); // access n[1][] via p
    int x = n[1][2]; // applying [] again to the array n[1]
    printf("%d\n", x);
 
    printf("%c %c\n", "abc"[2], 2["abc"]); // string literals are arrays too
}

输出

3 3
4 5 6
6
c c

[编辑] 解引用

解引用间接访问 表达式具有以下形式

* 指针表达式

其中

指针表达式 - 任何指针类型的 表达式

如果 指针表达式 是指向函数的指针,则解引用运算符的结果是该函数的函数设计器。

如果 指针表达式 是指向对象的指针,则结果是一个 左值表达式,它指定指向的对象。

解引用空指针、指向对象生命周期之外的对象的指针(悬空指针)、未对齐的指针或具有不确定值的指针是未定义的行为,除非解引用运算符被通过对其结果应用取地址运算符来抵消,如 &*E.

#include <stdio.h>
int main(void)
{
    int n = 1;
    int* p = &n;
    printf("*p = %d\n", *p); // the value of *p is what's stored in n
    *p = 7; // *p is lvalue
    printf("*p = %d\n", *p);
}

输出

*p = 1
*p = 7

[编辑] 取地址

取地址表达式具有以下形式

& 函数 (1)
& 左值表达式 (2)
& * 表达式 (3)
& 表达式 [ 表达式 ] (4)
1) 函数的地址
2) 对象的地址
3) 特殊情况:&* 相互抵消,两者都不会被评估
4) 特殊情况:&[] 中隐含的 * 相互抵消,只评估 [] 中隐含的加法。

其中

左值表达式 - 任何类型的 左值 表达式,它不是 位域 并且没有 寄存器 存储类

取地址运算符生成其操作数的 非左值 地址,适用于初始化指向操作数类型的指针。如果操作数是函数设计器 (1),则结果是指向函数的指针。如果操作数是对象 (2),则结果是指向对象的指针。

如果操作数是解引用运算符,则不会执行任何操作(因此可以将 &* 应用于空指针),只是结果不是左值。

如果操作数是数组索引表达式,则除了数组到指针转换和加法之外,不会执行任何操作,因此 &a[N] 对大小为 N 的数组有效(获得指向最后一个元素之后的指针是可以的,但解引用它是不行的,但在本表达式中解引用被抵消了)。

int f(char c) { return c;}
int main(void)
{
   int n = 1;
   int *p = &n; // address of object n
   int (*fp)(char) = &f; // address of function f
   int a[3] = {1,2,3};
   int *beg=a, *end=&a[3]; // same as end = a+3
}

[编辑] 成员访问

成员访问表达式具有以下形式

表达式 . 成员名称

其中

表达式 - 类型为 结构体联合体 的表达式
成员名称 - 一个 标识符,它命名由 表达式 指定的结构体或联合体的成员

成员访问表达式指定由其左操作数指定的 结构体联合体 的命名成员。它具有与其左操作数相同的 值类别

如果左操作数是 constvolatile 限定的,则结果也是限定的。如果左操作数是 原子,则行为是未定义的。

注意:除了命名结构体或联合体类型对象的标识符之外,以下表达式可能具有结构体或联合体类型:赋值函数调用逗号运算符条件运算符复合字面量

#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, changes the member of s
    int n = f().x; // f() is an expression of type struct s
//  f().x = 1; // Error: this member access expression is not an lvalue
 
    const struct s sc;
//  sc.x = 3;  // Error: sc.x is const, can't be assigned
 
    union { int x; double d; } u = {1};
    u.d = 0.1; // changes the active member of the union
}

[编辑] 通过指针访问成员

成员访问表达式具有以下形式

表达式 -> 成员名称

其中

表达式 - 类型为指向 结构体联合体指针 的表达式
成员名称 - 一个 标识符,它命名由 表达式 指向的结构体或联合体的成员

通过指针访问成员表达式指定由其左操作数指向的 结构体联合体 类型的命名成员。它的值类别始终是 左值

如果左操作数指向的类型是 constvolatile 限定的,则结果也是限定的。如果左操作数指向的类型是 原子,则行为是未定义的。

#include <stdio.h>
struct s {int x;};
int main(void)
{
    struct s s={1}, *p = &s;
    p->x = 7; // changes the value of s.x through the pointer
    printf("%d\n", p->x); // prints 7
}

[编辑] 缺陷报告

以下更改行为的缺陷报告被追溯应用于先前发布的 C 标准。

DR 应用于 已发布的行为 正确的行为
DR 076 C89 不必要的间接访问不能被 & 抵消 已更改为可抵消

[编辑] 参考文献

  • C17 标准(ISO/IEC 9899:2018)
  • 6.5.2.1 数组下标(第 57-58 页)
  • 6.5.2.3 结构体和联合体成员(第 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 数组下标 (p: 70)
  • 6.5.2.3 结构体和联合体成员 (p: 72-74)
  • 6.5.3.2 地址和间接运算符 (p: 78-79)
  • C89/C90 标准 (ISO/IEC 9899:1990)
  • 3.3.2.1 数组下标
  • 3.3.2.3 结构体和联合体成员
  • 3.3.3.2 地址和间接运算符

[edit] 参见

常用运算符
赋值 自增
自减
算术 逻辑 比较 成员
访问
其他

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b

a[b]
*a
&a
a->b
a.b

a(...)
a, b
(type) a
a ? b : c
sizeof


_Alignof
(从 C11 开始)

C++ 文档 for 成员访问运算符