命名空间
变体
操作

指针声明

来自 cppreference.cn
< c‎ | language

指针是一种对象类型,它引用另一种类型的函数或对象,可能还会添加限定符。指针也可能不指向任何内容,这由特殊的空指针值表示。

目录

[编辑] 语法

在指针声明的声明语法中,type-specifier 序列指定指向的类型(可以是函数类型或对象类型,并且可能是不完整的),并且 declarator 具有以下形式

* attr-spec-seq(可选) qualifiers(可选) declarator

其中 declarator 可以是命名正在声明的指针的标识符,包括另一个指针声明符(这将指示指向指针的指针)

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)

出现在 * 和标识符(或其他嵌套声明符)之间的 qualifiers 限定了正在声明的指针的类型

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 的指针(可选地是 constvolatile 限定的),反之亦然

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,或任何其他值为零的整数常量)。静态初始化也会将指针初始化为它们的空值。

空指针可以指示对象的缺失或可用于指示其他类型的错误情况。一般来说,接收指针参数的函数几乎总是需要检查该值是否为 null,并以不同的方式处理这种情况(例如,free 在传递空指针时什么也不做)。

[编辑] 注意

虽然任何指向对象的指针都可以强制转换为指向不同类型的对象的指针,但解引用指向与对象的声明类型不同的类型的指针几乎总是未定义行为。有关详细信息,请参阅严格别名

可以向通过指针访问对象的函数指示这些指针不别名。有关详细信息,请参阅restrict

(自 C99 起)

数组类型的左值表达式在大多数上下文中使用时,会经历到指向数组第一个元素的指针的隐式转换。有关详细信息,请参阅数组

char *str = "abc"; // "abc" is a char[4] array, str is a pointer to 'a'

指向 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++ 文档 关于 指针声明