当前位置: 首页 > news >正文

C语言零基础第12讲:各类指针变量介绍与转移表应用

目录

1.字符指针变量

2.数组指针变量

3.二维数组传参的本质

4.函数指针变量

4.1 函数指针变量的创建

4.2 函数指针变量的使用

5.typedef关键字

6.函数指针数组

7.转移表


正文开始

1.字符指针变量

        char*的一般使用如下:

        我们再看另一种使用方式:

        在《剑指offer》一书中收录了一道和字符串相关的笔试题,我们可以来看看:

2.数组指针变量

        指针数组:存放指针的数组。

        数组指针是什么呢?

        我们可以类比一下:字符指针char*用来存放字符的地址,整型指针int*用来存放整型的地址......那么数组指针用来存放数组的地址,指向的是数组

        数组指针可以用来遍历数组:

        这种遍历数组的方式很别扭, 那么数组指针到底有啥用呢?

        其实,数组指针,在二维数组传参的时候使用,才更容易理解。

3.二维数组传参的本质

        我们来看有一个二维数组传参的例子:

        总结:二维数组传参,本质上是传递了第一行这个一维数组的地址,形参可以写成数组的形式,也可以写成数组指针的形式。

       

4.函数指针变量

        顾名思义,函数指针变量是用来存放函数地址的,能够通过地址来调用函数

        函数是有地址的,我们可以看一个测试:

4.1 函数指针变量的创建

        那么,我们怎么把函数的地址存放在变量中呢?请看代码:

4.2 函数指针变量的使用

        那么,怎么通过函数指针来调用函数呢?请看代码:

        接下来,我们可以看2段出自《C陷阱和缺陷》一书的代码,看看如何理解?

代码1:
(* ( void(*)() ) 0)();代码2:
void (* signal( int, void(*)(int) ) ) (int);

        对于代码1,( void(*)() ) 0 是把0强制转换为函数指针类型,意思是让想0做函数的地址,这个函数的返回类型是void,没有参数。然后解引用,找到这个函数,再进行调用。当然了,解引用的*其实可以不用写。

        对于代码2,signal( int, void(*)(int) )是函数的声明,其中signal是函数名,int和void(*)(int)是参数。我们知道,函数的声明包括3个部分:1.函数名,2.参数,3.返回值类型。显然,还缺少返回值类型部分。而剩余的部分,void (*...)(int)  则是返回类型,即 void(*)(int)。为什么返回值类型的位置这么奇怪呢?因为在C语言中,函数指针的声明必须用括号强制优先级。

5.typedef关键字

        typedef是用来对类型进行重命名的,可以将复杂的类型简单化。 

        比如,如果你觉得unsigned int写起来不方便,想要简写成uint,可以这样做:

        我们还可以对一般的指针变量进行重命名:

        函数指针变量的类型,是挺复杂的,我们就可以进行重命名:

        对于前面的代码:void (* signal( int, void(*)(int) ) ) (int);,signal的第二个参数void(*)(int)是一个函数指针变量,我们可以对其重命名为pf_t,可是该怎么写呢?

        这样的话,void (* signal( int, void(*)(int) ) ) (int); 就可以简化为:pf_t signal(int, pf_t),其中pf_t是void(*)(int)。

6.函数指针数组

        对于指针数组,可以把指针存放到数组中。

        比如:int* arr[5];,float* arr[5];......

        函数指针也是指针,也是可以放在数组中的,这样的数组叫函数指针数组。

        请看下图:

7.转移表

        函数指针数组有啥用呢?转移表就是它的一种用途。

        我们来看一个例子,计算器的一般实现方式:

#include <stdio.h>void menu()				//菜单
{printf("*************************************\n");printf("****** 1.add    2.sub ***************\n");printf("****** 3.mul    4.div ***************\n");printf("******      0.exit    ***************\n");printf("*************************************\n");
}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 Div(int x, int y)	//除法函数
{return x / y;
}int main()
{int input = 0;int x = 0;int y = 0; int r = 0;do{menu();	//调用函数,打印出菜单界面printf("请选择:");scanf("%d", &input);switch (input){case 1:printf("请输入2个操作数:\n");scanf("%d %d", &x, &y);r = Add(x, y);			//调用Addprintf("r = %d\n", r);break;case 2:printf("请输入2个操作数:\n");scanf("%d %d", &x, &y);r = Sub(x, y);			//调用Sub	printf("r = %d\n", r);break;case 3:printf("请输入2个操作数:\n");scanf("%d %d", &x, &y);r = Mul(x, y);			//调用Mulprintf("r = %d\n", r);break;case 4:printf("请输入2个操作数:\n");scanf("%d %d", &x, &y);	r = Div(x, y);			//调用Divprintf("r = %d\n", r);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);return 0;
}

        我们来看一下运行效果:

        可是,如果我们要实现更多种计算,就需要继续补充对应的函数,然后switch语句越写越长。其实,我们可以不使用switch语句,请看代码:

#include <stdio.h>void menu()				//菜单
{printf("*************************************\n");printf("****** 1.add    2.sub ***************\n");printf("****** 3.mul    4.div ***************\n");printf("******      0.exit    ***************\n");printf("*************************************\n");
}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 Div(int x, int y)	//除法函数
{return x / y;
}int main()
{int (*pf[5])(int, int) = { NULL,Add,Sub,Mul,Div };	//将函数地址放在函数指针数组中,下标依次为:1,2,3,4,与菜单中的选项对应int input = 0;int x = 0;int y = 0;int r = 0;do{menu();	//调用菜单界面printf("请选择:");scanf("%d", &input);if (input >= 1 && input <= 4){printf("请输入2个操作数:\n");scanf("%d %d", &x, &y);r = pf[input](x, y);	//调用计算函数printf("r = %d\n", r);}else if (input == 0)printf("退出计算器\n");elseprintf("选择错误,请重新选择\n");} while (input);return 0;
}

        我们来看一下运行效果:

        这样改造代码之后,代码变得更加简洁了,好处在于,未来如果还需要增加更多的运算, 只需要增加函数和修改菜单,然后把函数的地址加到数组中即可,就不需要冗长的switch语句了。

        像这样,利用函数指针数组来实现分支逻辑的方式,一般叫做转移表,它有一种跳转的作用,通过下标找到函数地址,再通过函数地址去调用函数,替代了冗长的switch等语句

        所以,函数指针数组也是有用武之地的。

        不过,上面的代码只适用于2个整型的运算,所以转移表也有自己的局限性。


完结

        

        

        

        

        

        

http://www.dtcms.com/a/317764.html

相关文章:

  • 基于Github Pages搭建个人博客站点:hexo环境搭建、本地预览与发布
  • 11、系统配置System文件夹
  • Docker 数据存储路径(解决默认docker路径位置磁盘空间不足的情况)
  • React(二):jsx事件绑定、条件渲染、列表渲染、jsx的本质、购物车案例
  • 玳瑁的嵌入式日记D13-0806(C语言)
  • Spring Boot部门管理系统:查询、删除、新增实战
  • IntelliJ IDEA 2025.1.4.1 中文版
  • Nacos配置中心和数据隔离在idea中的实现
  • NWinfo(硬件信息检测工具)v1.4.20绿色免费版,U盘随走随检,结果即刻导出
  • 借助Aspose.OCR ,使用 Python 提取JPG图像文本、将JPG图像转换为Word
  • 本地服务器端部署基于大模型的通用OCR项目——dots.ocr
  • 达梦数据库数据守护集群启动与关闭标准流程
  • docker安装FFmpeg
  • Pytest项目_day06(requests中Session的用法)
  • 引领GameFi 2.0新范式:D.Plan携手顶级财经媒体启动“龙珠创意秀”
  • SpringMVC基础
  • 关于 idea 里 properties 文件的中文乱码问题
  • 「iOS」————单例与代理
  • 使用PHP与Apache实现服务器端文件管理
  • 19day-人工智能-机器学习-分类算法-决策树
  • docker 部署Bedrock Connector
  • 希尔排序:高效插入排序的进阶之道
  • 从零开始部署Qwen3-8b大模型到本地
  • Centos 安装 redis
  • 17_INIT_WORKLinux内核模块
  • prefetch 下载 GEO 数据注意事项
  • 设计模式—桥梁模式(Bridge)
  • 移动端跨平台框架(支持Harmony、iOS、Android)
  • 《第十篇》深入解析 `MilvusKBService`:基于 Milvus 的知识库服务实现
  • 在线计算“蛋白-蛋白复合物”的MM/GBSA