指针的声明与使用
指针的声明语法是 类型 *指针名,表示该指针指向该类型的变量。取地址运算符 & 获取变量的地址,解引用运算符 * 访问指针指向的内存。理解这些基本操作,是正确使用指针的前提。
声明指针
int *p; /* p 是指向 int 的指针 */
double *dp; /* dp 是指向 double 的指针 */
char *cp; /* cp 是指向 char 的指针 */
/* 同时声明多个指针 */
int *p1, *p2; /* p1 和 p2 都是 int* */
int *p3, x; /* p3 是 int*,x 是 int */
注意 int *p1, p2; 中 p2 是 int 不是 int*。每个指针都需要自己的 *。
取地址 &
& 运算符获取变量的内存地址:
int x = 42;
int *p = &x; /* p 存储 x 的地址 */
printf("%p\n", (void *)&x); /* x 的地址 */
printf("%p\n", (void *)p); /* p 的值(也是 x 的地址) */
& 只能用于左值(有内存地址的对象):
int *p = &10; /* 错误:10 是字面量,不是左值 */
int *p2 = &(x + 1); /* 错误:x+1 是表达式结果,不是左值 */
解引用 *
* 运算符访问指针指向的内存:
int x = 42;
int *p = &x;
printf("%d\n", *p); /* 42:访问 p 指向的值 */
*p = 100; /* 修改 p 指向的值(即 x) */
printf("%d\n", x); /* 100 */
解引用空指针或未初始化的指针是未定义行为:
int *p = NULL;
/* *p = 10; */ /* 崩溃 */
int *q;
/* *q = 10; */ /* 未定义行为:q 指向随机地址 */
指针赋值
指针之间可以赋值,只要类型兼容:
int x = 10, y = 20;
int *p = &x;
int *q = p; /* q 也指向 x */
printf("%d\n", *q); /* 10 */
q = &y; /* q 现在指向 y */
printf("%d\n", *q); /* 20 */
void* 可以指向任何类型,可以隐式转换为任何对象指针:
int x = 10;
void *vp = &x; /* void* 指向 int */
int *ip = vp; /* 隐式转换回 int* */
printf("%d\n", *ip); /* 10 */
多级指针
指针可以指向指针:
int x = 10;
int *p = &x; /* p 指向 x */
int **pp = &p; /* pp 指向 p */
printf("%d\n", x); /* 10 */
printf("%d\n", *p); /* 10 */
printf("%d\n", **pp); /* 10 */
常见错误
未初始化就解引用:
int *p;
printf("%d\n", *p); /* 未定义行为 */
混淆 * 在声明和表达式中的含义:
int x = 10;
int *p = &x; /* 声明:* 表示 p 是指针 */
*p = 20; /* 表达式:* 表示解引用 */
取地址的地址:
int x = 10;
int *p = &x;
int **pp = &&x; /* 错误:&& 不是合法运算符 */
int **pp = &(&x); /* 错误:&x 是右值(临时地址) */
/* 正确 */
int **pp = &p; /* p 是变量,&p 合法 */
最佳实践
- 声明时初始化指针
- 解引用前检查是否为 NULL
- 区分声明中的
*和表达式中的* - 用
void*作为通用指针,但转换回具体类型时要小心 - 多级指针不要超过 3 层,否则可读性太差