指针(pointer)是 C 语言中用于表示内存地址的一类变量。通过指针可以间接访问与修改数据、实现动态内存管理、将数组与函数参数高效串联,并作为链表、树等数据结构的基础。本节介绍指针的含义、取地址与解引用,以及若干常见指针类别与使用注意点。

指针的含义

程序运行时的内存由连续地址编号的存储单元组成。普通变量保存的是值;指针变量保存的是某个对象所在单元的地址,即“指向”该对象。

int *ptr = &var 存的是 var 的地址 int var = 10 位于某块内存地址 指向 *ptr 顺着地址取回 var 的值 → 10
ptr 保存 var 的地址;对 ptr 解引用(*ptr)可读取 var 的值。

声明指针时在类型后加 *:

int var = 10;
int *ptr = &var;

printf("%p\n", (void *)ptr);   /* 打印地址 */
printf("%d\n", *ptr);          /* 解引用,得到 10 */
  • 取地址运算符 &:&var 得到 var 的地址,可赋给同类型指针。指针声明后应初始化,若暂不使用可赋为 NULL。
  • 解引用运算符 *:在表达式中对指针使用 * 表示访问其所指对象。声明中的 * 表示该标识符是指针类型,与表达式中的解引用含义不同。

打印地址应使用格式符 %p,并将指针转换为 void *;用 %d 打印指针属于未定义行为。

通过指针修改所指对象

int a = 1;
int *pointer_to_a = &a;

a += 1;
*pointer_to_a += 1;

printf("a 现在等于 %d\n", a);   /* 3 */

*pointer_to_a += 1 修改的是指针所指的整型对象,与直接对 a 自增效果相同。这也是在函数中通过指针形参修改调用方变量的基础,详见按地址传参一节。

字符串与字符指针

字符串章节中的 const char *name = "John"; 可理解为:在只读存储区存放字符序列,栈上的指针变量 name 保存首字符的地址,随后通过 \0 判断结束。字符指针因此常用于遍历与传递字符串。

指针与下标的关系

数组元素的下标访问与指针算术、解引用可以对应:

char vowels[] = {'A', 'E', 'I', 'O', 'U'};
char *pv = vowels;

vowels[0];
*(pv + 0);
printf("%c\n", *pv);   /* A */

vowels[i] 与 *(vowels + i) 在常见情形下等价。数组名与指针的差异将在指针与数组中进一步说明。

指针的大小

指针变量所占字节数由运行环境决定,与所指类型无关:

int *ptr1;
char *ptr2;
printf("%zu\n", sizeof(ptr1));
printf("%zu\n", sizeof(ptr2));

同一平台上各类指针大小通常相同,因其保存的都是地址。

几类需区分的指针

  • 空指针 NULL:不指向有效对象;使用前宜判空。
  • void *:可保存任意类型对象的地址,解引用前须转换为具体类型,见void 指针。
  • 未初始化指针:指向不确定地址,解引用属于未定义行为;声明后应赋有效地址或 NULL。
  • 悬垂指针:所指内存已释放仍继续使用时产生;free 后宜将指针置为 NULL。

使用指针的利弊

指针支持动态内存、高效遍历数组与结构体、实现复杂数据结构;若管理不当,可能出现越界访问、悬垂引用或内存泄漏。应养成初始化、边界检查、配对 malloc/free 等习惯。

本节说明了指针存地址、& 取地址、* 解引用的基本规则。下一节讨论指针与数组的关系。