三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

嵌入式开发学习日志() day14 持续更新中

嵌入式开发学习日志() 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. 存储内容不同:二维数组保存的是字符串本身(拷贝了一份);指针数组保存的是字符串的首地址(引用)。
  2. 空间效率:如果要保存多个长度不一的字符串,使用指针数组更节省空间且操作更灵活。

二、二级指针

1. 什么是“二级指针”?

本质:它还是一个指针。
含义:它是一个“指向指针的指针”,即保存的是另一个指针变量的地址。

  • int **p:保存整型指针的地址。
  • char **p:保存字符型指针的地址。

2. 两大核心应用场景

  1. 修改主调函数中的指针变量:如果你想在被调函数中修改主调函数里定义的指针变量(例如让指针指向新的内存),必须传递该指针的地址(即二级指针)。
  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; // 错误!
  • 可以安全返回:

    1. static修饰的静态变量地址(生命周期贯穿程序始终)。
    2. 全局变量的地址。
    3. 字符串常量的地址(如return "hello world",存储在只读区)。
    4. 通过参数传递进来的地址(如strcpy返回dest)。
    5. 通过malloc在堆区申请且未被free的空间。

四、函数指针

1. 概念

本质:它是一个指针。
含义:指向函数的指针。函数名本身就是函数的入口地址。

2. 语法辨析

这是最容易混淆的地方,请注意括号的位置:

  • 函数声明:int fun(int, int);—— 声明一个名为 fun 的函数。
  • 指针函数:int *fun(int, int);—— 声明一个名为 fun 的函数,返回值是 int*。
  • 函数指针:int (*pfun)(int, int);—— 定义一个名为 pfun 的指针,指向参数为两个 int、返回值为 int 的函数。

总结口诀:看到(*名称),它就是指针;看到*名称(),它就是函数。

3. 使用步骤

  1. 定义:int (*pfun)(int, int) = NULL;(通常占 4 或 8 字节)。
  2. 赋值:
    int add(int n1, int n2) { ... } pfun = add; // 或者 pfun = &add;
  3. 调用:
    pfun(1, 2); // 等价于 (*pfun)(1, 2);

4. 高级应用:回调函数

函数指针最大的威力在于回调函数

  • 定义:将函数作为参数传递给另一个函数。
  • 目的:降低代码耦合度,实现多态效果,减少重复代码(例如qsort排序函数就是通过回调函数来决定排序规则的)。
← 返回列表