14、指针介绍
指针并不是一个能随意读取内存的数字。理解它,先抓住三件事:指针对象保存地址,类型决定怎样访问,所指对象必须仍然有效。
从一个 int 对象开始
本文以 C17 为基线。&value 取得对象的地址,int* ptr 声明一个能指向 int 的指针,*ptr 表示通过该指针访问所指对象。
指针变量和所指 int 是两个不同对象。改变 ptr 的值是改指向,改变 *ptr 的值是改被指对象,两种操作不要混淆。

取地址与解引用怎样配合
假设图中的 value 地址为 0x1000,这只是示意,不是某台机器的固定结果。ptr 保存这个地址,所以 *ptr = 20 会修改原来的 value,而 ptr 仍保存同一个地址。
* 在声明和表达式中作用不同:int* ptr 用于声明指针类型,表达式 *ptr 用于间接访问;& 在这里是取地址,而不是 C++ 引用声明。
C17 可运行示例
#include <assert.h>
#include <stdio.h>
void writeValue(int* target, int value) {
if (target != NULL) *target = value;
}
int main(void) {
int value = 100;
int* ptr = &value;
assert(*ptr == 100);
*ptr = 20;
assert(value == 20);
writeValue(ptr, 30);
assert(value == 30);
printf("%d\n", value);
int data[] = {10, 20, 30};
int* end = data + 3;
for (int* current = data; current != end; ++current) {
printf("%d ", *current);
}
printf("\n");
return 0;
}
第一行输出 30,第二行依次输出 10、20、30。函数参数 target 本身按值传递,函数仍能通过这个地址修改原对象;这不表示 C 函数有了 C++ 的引用参数。
指针运算为何不是简单的整数加一
在数组 data 中,data + 1 指向第二个元素,偏移单位由元素类型决定,不是固定加一个字节。可以形成数组尾后指针 data + 3 用来比较和结束遍历,但不能解引用它。
普通指针的加减、大小比较和相减应限制在标准允许的同一数组范围及尾后位置内。指向互不相关对象的地址,不能因为在本次运行中看起来相邻,就当成同一数组来计算。
空指针与生命周期边界
C 中用 NULL 表示空指针,C++17 通常用 nullptr。空指针不是“指向数值为0的int”;将指针设为空也不会释放它此前指向的动态存储。
sizeof 指针得到的是指针类型大小,不是数组长度,更不是可访问内存的容量。常见64位系统上指针常占8字节,但不要把这个实现事实写成所有平台的规定。
常见危险写法
未初始化的指针、空指针、已释放存储的指针,以及返回局部对象地址后继续使用,都不能安全解引用。下面这些情况应在文字或静态检查中识别,不通过执行未定义行为来“验证会输出什么”。
数组名在许多表达式中会转换成指向首元素的指针,但数组与指针不是同一种类型;数组大小不会随这个转换一同传进普通函数。
练习与依据
把 ptr 改指向另一个仍然有效的 int,比较修改 ptr 与修改 *ptr 的结果;再解释为什么尾后指针可以作为循环终点,却不能拿来读第四个元素。
示例按 C17 编译。标准版本见 WG14 项目列表;地址、间接访问和加法运算的基础条款可对照公开的 C11 草案 N1570,不要把它标成 C17 正式文本。本文只使用有效对象与有效范围,不为越界访问绘制确定结果。
