Pointer declaration
指针是一种对象类型,它引用函数或其他类型的对象,并可能添加限定符。指针也可能不引用任何对象,这种情况由特殊的空指针值表示。
目录 |
语法
在 声明语法 中,指针声明的 类型说明符 序列指定了被指向的类型(可以是函数或对象类型,也可以是不完整类型),而 声明符 具有以下形式:
*
属性说明符序列
(可选)
限定符
(可选)
声明符
|
|||||||||
其中 declarator 可以是命名所声明指针的标识符,包括另一个指针声明符(这将表示指向指针的指针):
float *p, **pp; // p 是指向 float 的指针 // pp 是指向 float 指针的指针 int (*fp)(int); // fp 是指向函数类型 int(int) 的指针
出现在
*
和标识符(或其他嵌套声明符)之间的
限定符
用于限定正在声明的指针类型:
int n; const int * pc = &n; // pc 是指向 const int 的非 const 指针 // *pc = 2; // 错误:不经过强制转换无法通过 pc 修改 n pc = NULL; // 正确:pc 本身可以被修改 int * const cp = &n; // cp 是指向非 const int 的 const 指针 *cp = 2; // 允许通过 cp 修改 n // cp = NULL; // 错误:cp 本身不可被修改 int * const * pcp = &cp; // 指向非 const int 的 const 指针的非 const 指针
attr-spec-seq (C23) 是一个可选的 属性 列表,应用于所声明的指针。
说明
指针用于实现间接访问,这是一种普遍存在的编程技术;它们可用于实现按引用传递语义、访问具有
指向对象的指针
指向对象的指针可以用 取址运算符 应用于对象类型表达式(可能不完整)的结果进行初始化:
int n; int *np = &n; // 指向int的指针 int *const *npp = &np; // 指向常量指针的非const指针,该常量指针指向非常量int int a[2]; int (*ap)[2] = &a; // 指向int数组的指针 struct S { int n; } s = {1} int* sp = &s.n; // 指向s成员int的指针
指针可以作为
解引用运算符
(一元
*
)的操作数出现,该运算符返回
左值
标识被指向对象:
int n; int* p = &n; // 指针 p 指向 n *p = 7; // 将 7 存储到 n 中 printf("%d\n", *p); // 左值到右值转换从 n 中读取值
指向
结构体
和
联合体
类型对象的指针也可以作为
通过指针访问成员
运算符
->
的左操作数出现。
由于 数组到指针 的隐式转换,数组首元素的指针可以通过数组类型的表达式进行初始化:
int a[2]; int *p = a; // 指向 a[0] 的指针 int b[3][3]; int (*row)[3] = b; // 指向 b[0] 的指针
某些 加法、减法 、 复合赋值 、 递增和递减 运算符被定义为用于指向数组元素的指针。
比较运算符 在以下情况下为对象指针定义:表示相同地址的两个指针比较结果相等,两个空指针值比较结果相等,指向同一数组元素的指针比较结果与这些元素的数组索引顺序一致,以及指向结构体成员的指针按照这些成员的声明顺序进行比较。
许多实现还提供随机来源指针的 严格全序关系 ,例如当它们被实现为连续("平坦")虚拟地址空间中的地址时。
指向函数的指针
指向函数的指针可以用函数的地址进行初始化。由于 函数到指针转换 规则,取址运算符是可选的:
void f(int); void (*pf1)(int) = &f; void (*pf2)(int) = f; // 与 &f 相同
与函数不同,函数指针是对象,因此可以存储在数组中、被复制、被赋值、作为参数传递给其他函数等。
指向函数的指针可用于 函数调用运算符 的左侧:这将调用被指向的函数:
#include <stdio.h> int f(int n) { printf("%d\n", n); return n * n; } int main(void) { int (*p)(int) = f; int x = p(7); }
解引用函数指针会产生指向函数的函数指示符:
int f(); int (*p)() = f; // 指针 p 指向函数 f (*p)(); // 通过函数指示符调用函数 f p(); // 直接通过指针调用函数 f
相等比较运算符 为指向函数的指针定义(若指向同一函数则比较结果为相等)。
由于 函数类型的兼容性 会忽略函数参数的顶层限定符,因此仅参数顶层限定符不同的函数指针可以互换:
int f(int), fc(const int); int (*pc)(const int) = f; // 正确 int (*p)(int) = fc; // 正确 pc = p; // 正确
指向 void 的指针
指向任何类型对象的指针都可以 隐式转换 为指向 void 的指针(可选地带有 const 或 volatile 限定符),反之亦然:
int n=1, *p=&n; void* pv = p; // int* 转换为 void* int* p2 = pv; // void* 转换为 int* printf("%d\n", *p2); // 输出 1
void指针用于传递未知类型的对象,这在泛型接口中很常见: malloc 返回 void * , qsort 要求用户提供的回调函数接受两个 const void * 参数。 pthread_create 要求用户提供的回调函数接受并返回 void * 。在所有情况下,调用方都有责任在使用前将指针转换为正确的类型。
空指针
每种类型的指针都有一个特殊值,称为该类型的 空指针值 。值为空的指针不指向任何对象或函数(解引用空指针是未定义行为),并且与所有同为 空 值的同类型指针比较结果相等。
要将指针初始化为空或将空值赋给现有指针,可使用空指针常量( NULL ,或任何其他值为零的整型常量)。 静态初始化 同样会将指针初始化为其空值。
空指针可以表示对象不存在,也可用于指示其他类型的错误条件。通常,接收指针参数的函数几乎总是需要检查该值是否为空,并以不同方式处理这种情况(例如,当传入空指针时, free 不会执行任何操作)。
注释
尽管任何指向对象的指针 都可以被转换 为指向不同类型对象的指针,但解引用与对象声明类型不符的指针几乎总是未定义行为。详见 严格别名规则 。
|
可以向通过指针访问对象的函数指示这些指针不会发生别名引用。详见 restrict 说明。 |
(since C99) |
数组类型的左值表达式,在大多数上下文中使用时,会经历向数组首元素指针的 隐式转换 。详见 数组 章节说明。
char *str = "abc"; // "abc" 是一个 char[4] 数组,str 是指向 'a' 的指针
指向 char 的指针常被 用于表示字符串 。要表示有效的字节字符串,指针必须指向作为 char 数组元素的 char,并且在大于或等于该指针所引用元素索引的某个位置,必须存在值为零的 char。
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 6.7.6.1 指针声明符 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.7.6.1 指针声明符 (p: 93-94)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.7.6.1 指针声明符 (p: 130)
- C99标准(ISO/IEC 9899:1999):
-
- 6.7.5.1 指针声明符(第115-116页)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 3.5.4.1 指针声明符
参见
|
C++ 文档
关于
指针声明
|