嵌入式开发学习日志() day14 持续更新中
一、指针数组
1. 什么是“指针数组”?
本质:它是一个数组。
特点:该数组中保存的每一个元素都是指针(地址)。
2. 定义语法
int *a[5];int *:表示数组中保存的数据类型是int *(整型指针)。[5]:表示该数组可以保存 5 个这样的指针。
3. 初始化方式
(1) 初始化为空
char *p = NULL; char *p[5] = {NULL}; // 将5个字符型指针全部初始化为 NULL(2) 初始化指向字符串常量
char *p[5] = {"hello", "world", "how", "are", "you"};p[0]保存字符串 "hello" 的首地址。p[1]保存字符串 "world" 的首地址。- ...以此类推。
4. 内存模型图解
当我们执行char *p[5] = {"hello", ...};时,内存布局如下:
- 左侧(栈区):
p数组本身存储的是一个个地址(如0x5000,0x3000等)。 - 右侧(字符串常量区):真正的字符串数据存储在只读区域,数组中的指针指向它们。
5. 重点辨析:指针数组 vs 二维字符数组
这是面试和考试中的高频考点:
// 二维字符数组 char str[3][100] = {"hello", "world", "how"}; sizeof(str) == 300 字节 // 3 * 100 // 指针数组 char *pstr[3] = {"hello", "world", "how"}; sizeof(pstr) == 24 字节 // 64位系统下 3 * 8区别总结:
- 存储内容不同:二维数组保存的是字符串本身(拷贝了一份);指针数组保存的是字符串的首地址(引用)。
- 空间效率:如果要保存多个长度不一的字符串,使用指针数组更节省空间且操作更灵活。
二、二级指针
1. 什么是“二级指针”?
本质:它还是一个指针。
含义:它是一个“指向指针的指针”,即保存的是另一个指针变量的地址。
int **p:保存整型指针的地址。char **p:保存字符型指针的地址。
2. 两大核心应用场景
- 修改主调函数中的指针变量:如果你想在被调函数中修改主调函数里定义的指针变量(例如让指针指向新的内存),必须传递该指针的地址(即二级指针)。
- 接收指针数组的数组名:指针数组的数组名在作为参数传递时,本质上就是一个二级指针。
char *str[3] = {"hello", "world", "how"}; // str 的类型就是 char **3. 传参机制图解(值传递 vs 地址传递)
理解二级指针的关键在于理解 C 语言的函数传参机制。
(1) 普通变量的传递
- 值传递:
fun(n)-> 无法修改主调函数的n。 - 地址传递(一级指针):
fun(&n)-> 可以修改主调函数的n。
(2) 指针变量的传递
值传递(传一级指针):
void fun(char *pstr) { pstr = "world"; } // 仅修改了形参,实参不变结果:主调函数中的指针依然指向 "hello"。
地址传递(传二级指针):
void fun(char **pstr) { *pstr = "world"; } // 通过解引用,修改了实参指向的内容结果:主调函数中的指针被成功修改为指向 "world"。
三、指针函数
1. 概念
本质:它是一个函数。
定义:返回值类型是指针类型的函数。
2. 返回值的注意事项(避坑指南)
指针函数能返回什么样的指针?这是一个关于生命周期的问题。
❌绝对不能返回:局部变量的地址(局部变量、局部数组)。因为函数结束后,栈内存被释放,返回的地址将变成野指针。
char str[32] = "hello world"; // 局部数组 return str; // 错误!✅可以安全返回:
static修饰的静态变量地址(生命周期贯穿程序始终)。- 全局变量的地址。
- 字符串常量的地址(如
return "hello world",存储在只读区)。 - 通过参数传递进来的地址(如
strcpy返回dest)。 - 通过
malloc在堆区申请且未被free的空间。
四、函数指针
1. 概念
本质:它是一个指针。
含义:指向函数的指针。函数名本身就是函数的入口地址。
2. 语法辨析
这是最容易混淆的地方,请注意括号的位置:
- 函数声明:
int fun(int, int);—— 声明一个名为 fun 的函数。 - 指针函数:
int *fun(int, int);—— 声明一个名为 fun 的函数,返回值是 int*。 - 函数指针:
int (*pfun)(int, int);—— 定义一个名为 pfun 的指针,指向参数为两个 int、返回值为 int 的函数。
总结口诀:看到
(*名称),它就是指针;看到*名称(),它就是函数。
3. 使用步骤
- 定义:
int (*pfun)(int, int) = NULL;(通常占 4 或 8 字节)。 - 赋值:
int add(int n1, int n2) { ... } pfun = add; // 或者 pfun = &add; - 调用:
pfun(1, 2); // 等价于 (*pfun)(1, 2);
4. 高级应用:回调函数
函数指针最大的威力在于回调函数。
- 定义:将函数作为参数传递给另一个函数。
- 目的:降低代码耦合度,实现多态效果,减少重复代码(例如
qsort排序函数就是通过回调函数来决定排序规则的)。
