命名空间
变体
操作

成员访问运算符

来自 cppreference.cn
< c‎ | 语言

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

运算符 运算符名称 示例 描述
[] 数组下标 a[b] 访问数组 a 的第 b 个元素
* 指针解引用 *a 解引用指针 a 以访问它所指向的对象或函数
& 取地址 &a 创建一个指向对象或函数 a 的指针
. 成员访问 a.b 访问 structunion a 的成员 b
-> 通过指针访问成员 a->b 访问指针 a 所指向的 structunion 的成员 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\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

[编辑] 解引用

解引用间接表达式的形式为

* pointer-expression

其中

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

如果 pointer-expression 是函数指针,则解引用运算符的结果是该函数的函数指示符。

如果 pointer-expression 是对象指针,则结果是指定所指向对象的左值表达式

解引用空指针、指向生命周期之外对象的指针(悬空指针)、未对齐的指针或具有不确定值的指针是未定义行为,除非解引用运算符通过对其结果应用取地址运算符而无效,例如 &*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

[编辑] 取地址

取地址表达式的形式为

& 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; // 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
}

[编辑] 成员访问

成员访问表达式的形式为

expression . member-name

其中

表达式 - structunion 类型的表达式
member-name - 一个标识符,表示由 expression 指定的结构体或联合体的成员

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

如果左操作数具有 constvolatile 限定符,则结果也具有相应的限定符。如果左操作数是 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; // 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
}

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

成员访问表达式的形式为

expression -> member-name

其中

表达式 - 指向 structunion指针类型的表达式
member-name - 一个标识符,表示由 expression 指向的结构体或联合体的成员

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

如果左操作数所指向的类型具有 constvolatile 限定符,则结果也具有相应的限定符。如果左操作数所指向的类型是 atomic,则行为未定义。

#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 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 数组下标 (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 地址和间接运算符

[编辑] 另请参阅

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

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 起)
(直至 C23)

alignof
(自 C23 起)

C++ documentation for 成员访问运算符