指针是存储内存地址的变量。理解指针的关键在于区分“指针本身的值(地址)”与“指针指向的对象(解引用后的值)”。
int a = 42;
int *p = &a; // p 保存 a 的地址
int *q = NULL; // 空指针,表示“不指向任何对象”
* 在声明中表示“这是一个指针类型”,在表达式中表示“解引用/取值”。
printf("%p\n", (void*)p); // 打印地址,需转 void*
printf("%d\n", *p); // 解引用:读取 a 的值 42
*p = 100; // 解引用赋值:修改 a 为 100
a == &a[0]。a[i] 等价于 *(a + i),编译器自动按 sizeof(int) 缩放偏移。sizeof(a) 给出整个数组大小,而 sizeof(p) 仅为指针大小(通常 8 字节)。int *arr = malloc(10 * sizeof(int));
if (!arr) { perror("malloc"); return 1; }
for (int i=0; i<10; ++i) arr[i] = i*i;
free(arr);
arr = NULL; // 防野指针
永远检查 malloc 返回值;释放后置 NULL 是良习。
| 声明 | 含义 |
|---|---|
const int *p / int const *p | 指向常量的指针:不可通过 p 修改值,p 可指向别处 |
int *const p | 常量指针:p 本身不可改指向,但可修改指向的值 |
const int *const p | 常量指针指向常量:双重不可变 |
口诀:* 左边的 const 修饰指向的值,右边的修饰指针本身。
int a = 10;
int *p = &a;
int **pp = &p; // 二级指针
**pp = 20; // 修改 a
常用于函数修改实参指针(如 void init(int **out))、动态二维数组、链表节点删除等场景。
scanf("%d", p) 忘记取地址 → 应为 &p 或直接传指针变量。valgrind --leak-check=full 检测内存错误。glibc、Linux 内核 中经典数据结构的指针操作。指针看似晦涩,实则是对内存模型的直接映射。牢记“地址、类型、生命周期”三要素,多练、多调试、多读优秀代码,指针终将成为你手中最锋利的工具。