命名空间
变体
操作

指针声明

来自 cppreference.cn
< c‎ | language

指针是一种对象类型,它引用一个函数或另一个类型的对象,可能添加限定符。指针也可以不引用任何东西,这由特殊的空指针值指示。

目录

[编辑] 语法

在指针声明的声明语法中,类型指定符序列指定了所指向的类型(可以是函数类型或对象类型,并且可以是不完整的),而声明符的形式是

* 属性指定符序列 (可选) 限定符 (可选) 声明符

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

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

属性指定符序列(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 元素,并且在大于或等于指针引用的元素索引的某个索引处必须有一个值为零的 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++ 文档,关于 指针声明