命名空间
变体
操作

成员访问运算符

来自 cppreference.cn
< c‎ | language

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

运算符 运算符名称 示例 描述
[] 数组下标 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 的结果)

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

#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

其中

expression - 结构体联合体类型的表达式
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

其中

expression - 指向结构体联合体指针类型的表达式
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 应用于 已发布行为 正确行为
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
(since C11)
(until C23)

alignof
(since C23)

C++ 文档 关于 Member access operators