深入理解C语言指针(三):数组指针、函数指针与转移表

深入理解C语言指针(三)

1. 字符指针

1
2
3
char ch = 'A';
char *p = &ch;
*p = 'B';

字符指针也常指向字符串字面量:

1
2
const char *message = "hello";
printf("%s\n", message);

字符串字面量不应被修改,因此推荐使用 const char *。下面两种形式含义不同:

1
2
char str[] = "hello";        // 创建可修改的字符数组
const char *pstr = "hello"; // 指向不可修改的字符串字面量

比较字符串内容应使用 strcmp,不能使用 == 比较指针地址。

2. 数组指针

数组指针是指向数组的指针:

1
2
3
int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &arr;
printf("%d\n", (*p)[2]);

int (*p)[5] 的阅读顺序:p 是指针,指向一个由5个 int 组成的数组。

3. 二维数组传参

二维数组传入函数后,外层数组名转换为指向第一行的指针:

1
2
3
4
5
6
7
8
9
void print_matrix(int (*matrix)[4], int rows)
{
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < 4; ++j)
printf("%d ", matrix[i][j]);
printf("\n");
}
}

也可以写成:

1
void print_matrix(int matrix[][4], int rows);

列数不能随意省略,因为编译器需要用它计算 matrix + 1 应跨过多少字节。

4. 函数指针

函数在内存中也有地址,函数指针用于保存该地址:

1
2
3
4
5
6
7
int add(int x, int y)
{
return x + y;
}

int (*operation)(int, int) = add;
int result = operation(3, 5);

函数指针常用于回调、排序规则、事件处理和状态机。

4.1 回调示例

1
2
3
4
int calculate(int x, int y, int (*op)(int, int))
{
return op(x, y);
}

调用者把行为作为参数传入,实现数据处理逻辑与具体运算解耦。

5. 函数指针数组

1
2
3
4
5
int add(int x, int y) { return x + y; }
int sub(int x, int y) { return x - y; }
int mul(int x, int y) { return x * y; }

int (*operations[3])(int, int) = {add, sub, mul};

operations 是数组,每个元素都是类型为 int (*)(int, int) 的函数指针。

6. 转移表实现计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

int add(int x, int y) { return x + y; }
int sub(int x, int y) { return x - y; }
int mul(int x, int y) { return x * y; }
int divide(int x, int y) { return x / y; }

int main(void)
{
int (*table[])(int, int) = {NULL, add, sub, mul, divide};
int choice = 0, x = 0, y = 0;

printf("1.加 2.减 3.乘 4.除\n请选择:");
if (scanf("%d", &choice) != 1 || choice < 1 || choice > 4)
return 1;

printf("请输入两个整数:");
if (scanf("%d%d", &x, &y) != 2)
return 1;
if (choice == 4 && y == 0)
{
printf("除数不能为0。\n");
return 1;
}

printf("结果:%d\n", table[choice](x, y));
return 0;
}

转移表用数组下标直接选择函数,减少大量重复的 switch 分支。它适合命令分发、协议处理、状态机和菜单系统。

7. 复杂声明的阅读方法

1
2
3
4
int (*p)[5];                 // 指向含5个int的数组
int *p2[5]; // 含5个int指针的数组
int (*func)(int, int); // 函数指针
int (*table[4])(int, int); // 函数指针数组

从变量名开始,先处理括号和下标,再向外阅读。复杂类型可以使用 typedef 简化:

1
2
typedef int (*BinaryOperation)(int, int);
BinaryOperation table[4];

8. 总结

高级指针的价值不在复杂语法,而在于表达“数据布局”和“可替换行为”。数组指针解决多维数据寻址,函数指针与转移表则为回调和模块化分发提供基础。