指针重难点:从字符指针到函数指针与转移表
专栏:C语言
C语言:指针4
本文为 C 语言指针系列第四篇,深度精讲 字符指针、 数组指针、 二维数组传参本质、 函数指针、 函数指针数组、 转移表六大核心考点,我会以严谨底层逻辑帮助大家尽可能的吸收、消化并化为己用
前言
在前面的指针学习中,我们已经掌握了指针基础、指针与数组、二级指针等核心内容,完成了指针入门阶段的积累。而从本篇开始,我们将正式进入指针高阶应用学习 —— 字符指针的底层存储、数组指针与指针数组的本质区分、二维数组传参的真实逻辑、函数指针与转移表的实战用法。这些内容既是理解 C 语言内存模型的关键,也是笔试面试与工程开发的高频考点,跟着本篇思路稳步学习,就能顺利打通指针进阶的全链路。
一.字符指针变量
字符指针 char* 是C语言中使用最频繁的指针类型之一,既可以指向单个字符,也可以指向字符串。
1.1基本用法
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
pc存放字符变量ch的地址- 通过
*pc可修改指向的字符
1.2指向常量字符串
int main()
{
const char* pstr = "hello bit.";
printf("%s\n", pstr);
return 0;
}
核心要点
- 并非把字符串存入指针,而是将字符串首字符地址存入
pstr。 - 字符串常量存储在只读数据区,不允许修改,必须用
const修饰。 - 相同常量字符串在内存中只存一份。
1.3经典笔试题
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
if(str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
运行结果:

代码分析:
str1、str2是数组,各自独立开辟内存,地址不同。str3、str4指向只读区同一个常量字符串,地址相同。
二.数组指针变量
数组指针是指向数组的指针,本质是指针,专门存放整个数组的地址(&数组名)。
2.1定义格式
int (*p)[10];
逻辑分析:
p先与*结合,说明是指针。- 指向一个包含 10 个
int元素的数组。 - 必须加括号,因为
[ ]优先级高于*。
2.2指针数组与数组指针
指针数组与数组指针的区分在上一章节我们已经讲过了,这里再重复说明一遍
int *p1[10];// 指针数组
int (*p2)[10];//数组指针
指针数组:是数组,里面每个元素是指针
数组指针: int(*p)[10] 是指针,指向整个数组
区分口诀:括号优先是指针,括号靠后是数组
2.3数组指针初始化
int arr[10] = {0};
int (*p)[10] = &arr;
·&arr· 是整个数组的地址,类型为 ·int(*)[10]·。
一维数组极少使用,二维数组传参是核心应用场景。
三.二维数组传参的本质
核心结论:
- 二维数组可以看作 “元素为一维数组” 的一维数组。
- 二维数组名表示第一行一维数组的地址,类型为数组指针。
3.1形参两种等价写法
写法1:数组形式
void test(int a[3][5], int row, int col);
写法2:指针形式(本质)
void test(int (*p)[5], int row, int col);
3.2举例
#include <stdio.h>
void test(int (*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for(i = 0; i < r; i++)
{
for(j = 0; j < c; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);
return 0;
}
分析:
1. p + i:指向第 i 行。
2. *(p + i):第 i 行首元素地址。
3. *(*(p + i) + j):第 i 行第 j 列元素。
运行结果:

四.函数指针变量
函数指针是指向函数的指针,用于存放函数地址,并通过地址调用函数。
4.1函数地址
函数名 == 函数地址
&函数名 == 函数名
例如:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test: %p\n", test);
printf("&test: %p\n", &test);
return 0;
}
4.2函数指针定义
举例:
int Add(int x, int y)
{
return x + y;
}
//定义函数指针
int (*pf)(int, int) = Add;
其中:
int:函数返回值类型(*pf):pf是函数指针(int, int):函数参数列表
4.3函数指针调用
举个例子:
int main()
{
int (*pf)(int, int) = Add;
printf("%d\n", (*pf)(2, 3));//<---标准调用
printf("%d\n", pf(3, 5));//<---简化调用(*可省略)
return 0;
}
4.4经典代码(C陷阱与缺陷)
4.4.1经典1
(*(void (*)())0)();
分析:
- 将
0强制转为void(*)()类型函数地址 - 调用 0 地址处的无参无返回值函数
4.4.2经典2
void (*signal(int, void(*)(int)))(int);
分析:
- 声明
signal函数 - 参数:
int+ 函数指针void(*)(int) - 返回值:函数指针
void(*)(int)
typedef简化
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
五.函数指针数组
将多个同类型函数地址存入数组,即为函数指针数组。
5.1定义格式
举个例子:
int (*pArr[5])(int, int);
分析:
pArr是数组- 每个元素为
int(*)(int, int)类型函数指针
但是有一个要求,就是所有函数的返回值、参数个数、参数类型必须完全一致。
六.转移表
我们一般写简单计算器的实现的代码时就会想到使用 switch 语句来实现,但是写完之后代码很长很麻烦,效率低;那我们如何解决这个问题呢?那就是利用转移表了,转移表用函数指针数组替代大 switch / case,代码更简洁、扩展性更强。
接下来请看代码如何利用转移表来实现的
计算器的实现(利用转移表)
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }
int main()
{
int x, y;
int input = 1;
int ret = 0;
// 转移表
int(*p[5])(int, int) = { 0, add, sub, mul, div };
do
{
printf("*************************\n");
printf(" 1:add 2:sub\n");
printf(" 3:mul 4:div\n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
printf("ret = %d\n", ret);
}
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("输入错误\n");
}
} while (input);
return 0;
}
运行结果:

优点:
- 代码简洁优雅
- 新增功能只需添加函数与数组元素
- 工程与面试高频用法
总结
char*指向字符串时存储首字符地址,常量字符串必须用const修饰,相同字符串只存一份。- 数组指针
int(*p)[10]是指向数组的指针,主要用于二维数组传参。 - 二维数组名是第一行地址,类型为数组指针,传参时可用数组或指针形式接收。
- 函数名即函数地址,函数指针可存储并调用函数,
*可省略。 - 函数指针数组可实现转移表,替代冗长
switch/case。 typedef可大幅简化复杂指针类型的书写。
易错点
- 混淆指针数组与数组指针,导致定义错误。
- 二维数组传参误用
int**,造成程序崩溃。- 忽略字符串常量只读属性,试图修改导致程序异常。
- 函数指针参数 / 返回值不匹配,编译失败。
- 函数指针数组内存放不同类型函数,引发未定义行为。
如果这篇文章对友友们有帮助,期待友友们的支持!!!
点赞 ❤️ 干货收藏,助力指针吃透
收藏 📂 反复回看,攻克难点盲区
关注 🔔 持续更新,C 语言不迷路
留言 💬 交流探讨,学习共同进步
友友们的每一次互动,都是我持续更新C语言硬核知识的动力!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)