嵌入式学习 day13:指针进阶
一、void指针
1. 概念
void *是无类型指针(泛型指针),可以存放任意类型指针的值。
c
void *p; // 无类型指针 char *cp; int *ip; double *dp; p = cp; // ✅ 任意指针都能赋给void * p = ip; p = dp;
2. 使用场景
(1)存放任意内存地址
c
void *p = (void *)0x12345678; // 存放一个绝对地址
(2)作为函数参数传递任意类型指针
c
void swap(void *a, void *b, int size) { // 按字节交换内存块 }void *类型的参数可以接收任意类型的指针,配合size参数可以实现"通用交换函数"。
void指针不能直接解引用,必须先转换为具体类型:
c
int num = 10; void *p = # printf("%d\n", *p); // ❌ void *不能解引用 printf("%d\n", *(int *)p); // ✅ 先转为int *再解引用二、二级指针
1. 概念
二级指针是指向指针的指针,存的是指针变量的地址。
c
int a = 100; int *p = &a; // p指向a int **q = &p; // q指向p
逐级解引用:
c
printf("%d\n", **q); // 输出100理解过程:
q存的是p的地址*q取到p的内容(即a的地址)**q取到a的内容(即100)
2. 二级指针的用途
(1)修改指针变量的指向
函数内想修改函数外指针变量的值,需要传指针变量的地址(即二级指针)。
c
void allocate(int **pp, int size) { *pp = (int *)malloc(size * sizeof(int)); } int main(void) { int *p = NULL; allocate(&p, 10); // 传入p的地址 // p现在指向动态分配的空间 return 0; }(2) 指针数组传参
c
void print_strings(char **str, int len) { for (int i = 0; i < len; i++) { printf("%s\n", str[i]); } } int main(void) { char *str[3] = {"hello", "world", "C"}; print_strings(str, 3); // 指针数组名→char** return 0; }三、指针数组和数组指针
1. 指针数组(本质是数组)
数组的每个元素都是指针。
c
int *a[5]; // 有5个元素,每个都是 int* 类型,占40字节(64位系统)
使用场景:操作字符串数组(比二维字符数组更高效)。
c
char *str[3] = {"hello", "world", "C"}; printf("%s\n", str[0]); // 输出hello指针数组只存地址(字符串在常量区),二维字符数组存完整副本(字符串在数组内)。指针数组更省空间,但指向的内容通常是只读的。
2. 数组指针(本质是指针)
指向数组的指针。
c
int (*p)[5]; // p指向一个有5个int元素的数组,p占8字节
与一维数组的关系:
c
int a[5] = {1, 2, 3, 4, 5}; int (*p)[5] = &a; // 指向整个数组之前学过:一维数组名a通常理解为int *(指向首元素),但有两种情况例外:
sizeof(a):计算整个数组大小&a:取整个数组的地址,类型为int (*)[5]
c
int a[5]; printf("%p\n", a); // 指向第一个元素 printf("%p\n", &a); // 指向整个数组,值相同但类型不同数组指针取*,值不变,类型降级:
c
int (*p)[5] = &a; printf("%p\n", *p); // 与a的值相同,类型变为 int *3. 指针数组 vs 数组指针
| 指针数组 | 数组指针 | |
|---|---|---|
| 写法 | int *a[5] | int (*p)[5] |
| 本质 | 数组 | 指针 |
| 运算符优先级 | []优先 | 括号优先 |
| 大小 | 5×8=40字节 | 8字节 |
四、二维数组与指针的关系
1. 二维数组的地址结构
c
int a[2][3] = {{1,2,3}, {4,5,6}};| 表达式 | 含义 | 类型 |
|---|---|---|
a | 指向第一行的数组指针 | int (*)[3] |
a[0] | 第一行第一个元素的指针 | int * |
a[1] | 第二行第一个元素的指针 | int * |
&a[0][0] | 第一个元素的指针 | int * |
二维数组的数组名a和一维数组的数组名a含义不同,一维数组名指向首元素,二维数组名指向第一行(整个行数组)。
2. 访问第m行第n列元素的等价写法
c
a[m][n] // 最直观 *(a[m] + n) // 先取第m行指针,再偏移 *(*(a + m) + n) // 先偏移到第m行,再解引用取行指针,再偏移 *(*(a + m) + n) // 通用公式
如果已知首元素指针p = &a[0][0],总列数为N:
c
*(p + m * N + n) // 按一维方式偏移
如果已知数组指针q = a:
c
*(*(q + m) + n) // 与上面第3种等价 *(q[m] + n) // q[m]等价于*(q+m)
3. 二维数组传参
二维数组传参需要传递数组指针和行数。
c
int fun(int (*parray)[3], int row); // 方式1:数组指针 int fun(int parray[][3], int row); // 方式2:二维数组形式(等价)
c
void print_array(int (*arr)[3], int row) { for (int i = 0; i < row; i++) { for (int j = 0; j < 3; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main(void) { int a[2][3] = {{1,2,3}, {4,5,6}}; print_array(a, 2); // 传数组名和行数 return 0; }二维数组传参时列数不能省略,但行数可以省略。
五、今日总结
今天的核心要点如下:
void指针:
无类型指针,可以存放任意类型指针
不能直接解引用,必须强转类型
常用于通用函数设计(如
qsort、memcpy)
二级指针:
指向指针的指针,存的是指针变量的地址
用途:修改函数外指针变量的指向、指针数组传参
解引用:
**q逐级取值
指针数组 vs 数组指针:
int *a[5]:指针数组(数组),存指针int (*p)[5]:数组指针(指针),指向数组区分关键:看运算符优先级和括号
二维数组与指针:
a是int (*)[N]类型(指向行的数组指针)a[i]是int *类型(指向第i行首元素)访问元素:
a[m][n]、*(a[m]+n)、*(*(a+m)+n)传参:列数不能省略,行数可省
今日感悟:
指针是c语言学习的一大难点。void *是实现泛型编程的基础;二级指针若没有理解,画图后思路就清晰了;指针数组和数组指针的区分关键在于运算符优先级;二维数组和指针的关系虽然复杂,但掌握a、a[i]、&a三者的类型区别,就能应对大多数场景。