指针声明
指针是一种指向函数或另一种类型对象的类型,可能添加限定符。指针也可能指向空值,这由特殊的空指针值表示。
内容 |
[编辑] 语法
在指针声明的 声明语法 中,类型说明符 序列指定指向的类型(可以是函数或对象类型,也可以是不完整的),声明符 具有以下形式
* attr-spec-seq (可选) 限定符 (可选) 声明符 |
|||||||||
其中 声明符 可以是命名要声明的指针的标识符,包括另一个指针声明符(这将指示指向指针的指针)
float *p, **pp; // p is a pointer to float // pp is a pointer to a pointer to float int (*fp)(int); // fp is a pointer to function with type int(int)
出现在 *
和标识符(或其他嵌套声明符)之间的 限定符 限定要声明的指针的类型
int n; const int * pc = &n; // pc is a non-const pointer to a const int // *pc = 2; // Error: n cannot be changed through pc without a cast pc = NULL; // OK: pc itself can be changed int * const cp = &n; // cp is a const pointer to a non-const int *cp = 2; // OK to change n through cp // cp = NULL; // Error: cp itself cannot be changed int * const * pcp = &cp; // non-const pointer to const pointer to non-const int
attr-spec-seq(C23) 是 属性 的可选列表,应用于已声明的指针。
[编辑] 解释
指针用于间接寻址,这是一种普遍的编程技术;它们可用于实现按引用传递语义,访问具有动态 存储期 的对象,实现“可选”类型(使用空指针值),结构体之间的聚合关系,回调(使用指向函数的指针),通用接口(使用指向 void 的指针),等等。
[编辑] 指向对象的指针
指向对象的指针可以使用应用于对象类型表达式(可以是不完整的)的 取地址运算符 的结果来初始化
int n; int *np = &n; // pointer to int int *const *npp = &np; // non-const pointer to const pointer to non-const int int a[2]; int (*ap)[2] = &a; // pointer to array of int struct S { int n; } s = {1} int* sp = &s.n; // pointer to the int that is a member of s
指针可能出现在 间接寻址运算符(一元 *
)的运算对象中,它返回标识指向对象的 左值
int n; int* p = &n; // pointer p is pointing to n *p = 7; // stores 7 in n printf("%d\n", *p); // lvalue-to-rvalue conversion reads the value from n
指向 结构体 和 联合体 类型的对象的指针也可以作为 通过指针访问成员 运算符 ->
的左侧运算对象。
由于 数组到指针 的隐式转换,指向数组第一个元素的指针可以用数组类型的表达式来初始化
int a[2]; int *p = a; // pointer to a[0] int b[3][3]; int (*row)[3] = b; // pointer to b[0]
某些 加法、减法、复合赋值、自增和自减 运算符是针对指向数组元素的指针定义的。
比较运算符 在某些情况下针对指向对象的指针定义:表示相同地址的两个指针比较相等,两个空指针值比较相等,指向相同数组元素的指针比较与这些元素的数组索引相同,指向结构体成员的指针按这些成员的声明顺序进行比较。
许多实现还提供了来自随机来源的指针的 严格的总排序,例如,如果它们在连续的(“平面”)虚拟地址空间内实现为地址。
[编辑] 指向函数的指针
指向函数的指针可以用函数的地址来初始化。由于 函数到指针 的转换,取地址运算符是可选的
void f(int); void (*pf1)(int) = &f; void (*pf2)(int) = f; // same as &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; // pointer p is pointing to f (*p)(); // function f invoked through the function designator p(); // function f invoked directly through the pointer
相等比较运算符 是针对指向函数的指针定义的(如果指向同一个函数,则它们比较相等)。
由于 函数类型的兼容性 会忽略函数参数的顶层限定符,因此指向参数仅在顶层限定符上不同的函数的指针是可互换的
int f(int), fc(const int); int (*pc)(const int) = f; // OK int (*p)(int) = fc; // OK pc = p; // OK
[编辑] 指向 void 的指针
任何类型的对象指针都可以 隐式转换为 指向 void 的指针(可以选择 const 或 volatile 限定),反之亦然
int n=1, *p=&n; void* pv = p; // int* to void* int* p2 = pv; // void* to int* printf("%d\n", *p2); // prints 1
指向 void 的指针用于传递类型未知的对象,这在通用接口中很常见: malloc 返回 void*,qsort 需要一个用户提供的回调,该回调接受两个 const void* 参数。 pthread_create 需要一个用户提供的回调,该回调接受和返回 void*。在所有情况下,调用者都有责任在使用之前将指针转换为正确的类型。
[编辑] 空指针
每种类型的指针都具有一个称为该类型的空指针值的特殊值。值为 null 的指针不指向任何对象或函数(对空指针进行解引用会导致未定义行为),并且与值为null 的相同类型的指针进行比较时结果相等。
要将指针初始化为空值或将空值分配给现有指针,可以使用空指针常量(NULL,或任何值为零的整数常量)。静态初始化 也将指针初始化为它们的空值。
空指针可以指示对象不存在,也可以用于指示其他类型的错误条件。通常,接收指针参数的函数几乎总是需要检查该值是否为空,并以不同的方式处理这种情况(例如,free 在传递空指针时不执行任何操作)。
[编辑] 注意事项
虽然任何指向对象的指针都可以 强制转换为 指向不同类型对象的指针,但对与对象声明类型不同的类型指针进行解引用几乎总是未定义的行为。有关详细信息,请参阅 严格别名。
可以通过指针访问对象的函数可以指示这些指针不发生别名。有关详细信息,请参阅 restrict。 |
(自 C99 起) |
在大多数情况下,数组类型的左值表达式会在进行 隐式转换 为指向数组第一个元素的指针时发生。有关详细信息,请参阅 数组。
char *str = "abc"; // "abc" is a char[4] array, str is a pointer to '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 指针声明符 (p: 115-116)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.5.4.1 指针声明符
[编辑] 另请参阅
C++ 文档 for 指针声明
|