目录
深入理解指针(精华笔记)
一、内存与地址的本质
编址与地址总线
二、指针基础概念与运算符
指针的定义与使用
指针变量的大小
三、指针类型与意义
四、const修饰指针的用法
五、指针运算的本质
常见的指针运算
六、数组名的本质与数组传参
数组名本质
数组传参本质
七、冒泡排序与数组指针应用实例
冒泡排序算法(优化版本)
八、高级指针:二级指针与指针数组
二级指针
指针数组
九、指针数组模拟二维数组
十、指针安全(野指针与assert断言)
野指针产生原因与规避方式
断言(assert)应用
十一、传值调用与传址调用
深入理解指针(精华笔记)
一、内存与地址的本质
- 内存:计算机内存划分为多个单元,每个单元大小为1个字节(byte)。
- 地址:每个内存单元都有一个编号,即地址(类似宿舍门牌号)。
编址与地址总线
- 计算机硬件通过地址总线实现编址:
- 32位机器 → 32根地址线 → 地址长度4字节(32bit)
- 64位机器 → 64根地址线 → 地址长度8字节(64bit)
- CPU通过地址总线与数据总线访问内存数据。
二、指针基础概念与运算符
指针的定义与使用
-
取地址运算符(&)
int a = 10; int *pa = &a; // 指针变量pa存储a的地址
-
解引用运算符(*)
- 通过地址访问或修改数据
*pa = 20; // 修改a为20
指针变量的大小
- 指针大小由地址线宽度决定:
- 32位平台:4字节
- 64位平台:8字节
- 与指针所指类型无关,同平台下指针变量大小相同。
三、指针类型与意义
-
指针类型决定:
- 解引用权限(访问字节数):
char*
一次访问1字节int*
一次访问4字节
- 指针算术运算的步长(±整数运算):
char*
类型指针+1
:地址+1字节int*
类型指针+1
:地址+4字节
- 解引用权限(访问字节数):
-
泛型指针(void)*
- 能指向任意类型数据
- 不能直接解引用,不能指针±整数运算
- 常用于泛型函数参数
四、const修饰指针的用法
写法 | 含义 |
---|---|
const int *p | 指针指向的内容不可改,指针可改 |
int *const p | 指针本身地址不可改,内容可改 |
const int *const p | 指针本身及指向内容都不可改 |
五、指针运算的本质
常见的指针运算
- 指针 ± 整数
- 常用于遍历数组
*(p+i) 等价于 p[i]
- 指针 - 指针
- 常用于求元素间距离,如字符串长度
strlen实现: return p - s;
- 指针关系运算
- 比较指针地址,常用于遍历数组判断结束
六、数组名的本质与数组传参
数组名本质
- 数组名本质是数组首元素地址,但有两个例外:
sizeof(数组名)
:表示整个数组大小&数组名
:取出的是整个数组的地址,&数组名+1
会跳过整个数组大小
- 其余任何地方,数组名都表示首元素地址
数组传参本质
- 一维数组传参本质上传递的是首元素地址
- 形参可以写作数组形式或指针形式:
void func(int arr[]) // 等价于 int *arr
- 函数内部无法直接获取数组元素个数,需额外传入元素个数。
七、冒泡排序与数组指针应用实例
冒泡排序算法(优化版本)
void bubble_sort(int arr[], int sz) {for(int i=0; i<sz-1; i++) {int flag = 1; // 假设已排序for(int j=0; j<sz-i-1; j++) {if(arr[j] > arr[j+1]) {int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp;flag = 0; //发生交换,非排序状态}}if(flag) break; // 无交换则停止排序}
}
八、高级指针:二级指针与指针数组
二级指针
- 指针变量的地址存放在另一个指针中形成二级指针:
int a = 10;
int *pa = &a;
int **ppa = &pa;
- 二级指针解引用:
*ppa
→ 得到pa
(一级指针)**ppa
→ 得到a
(原数据)
指针数组
- 存放多个指针的数组:
int a=1, b=2, c=3;
int* parr[] = {&a, &b, &c};
九、指针数组模拟二维数组
- 指针数组元素可指向多个一维数组:
int arr1[] = {1,2,3};
int arr2[] = {4,5,6};
int *parr[] = {arr1, arr2};for(int i=0; i<2; i++) {for(int j=0; j<3; j++)printf("%d ", parr[i][j]);
}
- 内存不连续,模拟二维数组访问模式。
十、指针安全(野指针与assert断言)
野指针产生原因与规避方式
- 原因:
- 指针未初始化
- 指针越界访问
- 访问已释放空间的指针
- 规避:
- 初始化指针为
NULL
- 使用前检查有效性(断言)
- 避免返回局部变量地址
- 初始化指针为
断言(assert)应用
- 检查运行时条件
assert(p != NULL);
- 调试时开启,发布时禁用(
#define NDEBUG
禁用)
十一、传值调用与传址调用
- 传值调用:修改形参不影响实参(值拷贝)
- 传址调用:函数形参接收实参地址,间接修改实参本身
void Swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp;
}