核心结论
- 优先级相同:取地址符
&和解引用符*的优先级相同 - 结合性:从右向左结合
- 重要对比:后缀操作符(
[]、()->、.)的优先级高于&和*
基本概念
| 运算符 | 名称 | 作用 | 示例 |
|---|---|---|---|
& |
取地址符 | 获取变量的内存地址 | &x 返回变量x的地址 |
* |
解引用符 | 访问指针指向的内存内容 | *p 返回指针p指向的值 |
优先级规则详解
1. 与后缀操作符的优先级比较
规则:后缀操作符优先级 > &和*优先级
示例分析
int arr[] = {10, 20, 30};
int *p = arr;
情况1:*p++
int value = *p++;
// 等价于:*(p++)
// 执行顺序:
// 1. p++ 先返回p的当前值(指向arr[0]),然后p自增指向arr[1]
// 2. * 对返回的地址解引用,得到arr[0]的值10
// 结果:value = 10, p指向arr[1]
情况2:(*p)++
int value = (*p)++;
// 执行顺序:
// 1. (*p) 先解引用得到arr[0]的值10
// 2. ++ 将该值增加,arr[0]变为11
// 结果:value = 10, arr[0]的值变为11
情况3:&arr[1]
int *ptr = &arr[1];
// 等价于:&(arr[1])
// 执行顺序:
// 1. arr[1] 先访问数组第二个元素
// 2. & 取该元素的地址
// 结果:ptr指向arr[1]
2. 连续使用&和*(从右向左结合)
规则:多个&和*连续出现时,从最右边开始计算
示例分析
int a = 100;
int *p = &a; // p指向a
int **pp = &p; // pp指向p
情况1:**pp
int value = **pp;
// 等价于:*(*pp)
// 执行顺序:
// 1. *pp 解引用得到p(即a的地址)
// 2. *(*pp) 再次解引用得到a的值100
// 结果:value = 100
情况2:&*p
int *q = &*p;
// 等价于:&(*p)
// 执行顺序:
// 1. *p 解引用得到变量a
// 2. &(*p) 对a取地址,结果等于p
// 结果:q的值与p相同,都指向a
常见应用场景
1. 函数中修改指针本身
void allocateMemory(int **ptr) {
*ptr = (int*)malloc(sizeof(int)); // 修改外部指针的指向
**ptr = 123; // 修改指针指向的值
}
int main() {
int *myPtr = NULL;
allocateMemory(&myPtr); // 传递指针的地址
printf("%d\n", *myPtr); // 输出:123
free(myPtr);
return 0;
}
2. 多级指针与字符串数组
char *names[] = {"Alice", "Bob", "Charlie"};
char **pp = names; // pp指向names[0]
// 获取"Bob"的第一个字符'B'
char first_char = *(*(pp + 1));
// 等价于:pp[1][0]
// 执行顺序:
// 1. pp + 1 指向names[1]
// 2. *(pp + 1) 解引用得到names[1](指向"Bob"的指针)
// 3. *(*(pp + 1)) 再次解引用得到'B'
易错点总结
| 表达式 | 实际含义 | 常见误解 |
|---|---|---|
*p++ |
*(p++) |
指针先移动,再取值 |
*p->data |
*(p->data) |
先访问成员,再解引用 |
&arr[i] |
&(arr[i]) |
先取元素,再取地址 |
**pp |
*(*pp) |
从右向左解引用 |
最佳实践
- 多用括号:在复杂表达式中使用括号明确优先级
- 分步编写:将复杂表达式拆分成多行,提高可读性
- 注释说明:对复杂的指针操作添加注释
// 不推荐:难以理解
int result = **++pp;
// 推荐:清晰明确
pp++; // 移动到下一个指针
int result = **pp; // 获取指向的值
// 或者使用括号
int result = *(*(++pp));
记忆技巧
- 后缀优先:
[]()->.总是先执行 - 向右看齐:
&和*从右向左计算 - 括号是友:不确定时就加括号
掌握这些规则后,面对复杂的指针表达式时就能游刃有余地分析了。