【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现1:https://developer.aliyun.com/article/1474740
switch实现计算器:
主要实现计算器程序思路:
- 定义了四个运算函数add、sub、mul、div实现四则运算。
- main函数中:
- 使用do while循环控制程序循环执行。
- 打印菜单让用户选择运算类型。
- 根据用户选择用switch case调用对应的运算函数。
- 每次运算前输入两个操作数,运算后打印结果。
- 选择0退出循环,退出程序。
#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; 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); switch (input)//选择 { case 1: printf("输入两个操作数:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2: printf("输入两个操作数:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("输入两个操作数:"); scanf("%d %d", &x, &y); ret = mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("输入两个操作数:"); scanf("%d %d", &x, &y); ret = div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); return 0; }
实现是实现了,但是
case
里面的每个代码块除了ret = (?)(x,y);
有点不同,其他都很相似,这么多代码重复写,会造成代码的冗余,如果我们又继续给用户增加功能,比如&,^,>>等等,然后一个功能我们就加一个case,case
多了,代码重复的也多咋改变呢?
不着急,我们不是学习了函数指针数组吗?
我们可以把函数的地址存储在数组里面,然后通过指针访问数组下标(0,1,2,3,4,5...),然后解引用找到我们要找到我们要实现函数的地址
然后给他传参,再接收他的计算的返回值不就搞定了。
哈哈哈哈!!掌声应该送给自己,说做就做!让我们继续往下走。
函数指针数组实现计算器:
思路:
定义了4个函数Add、Sub、Mul、Div,用于四则运算。
menu()函数打印菜单界面。
定义了一个函数指针数组pfArr,元素类型为int (*)(int, int),可以存储这4个二元运算函数的地址。
在主函数中使用do-while循环不断运行:
- 调用
menu()
打印菜单 scanf
输入选择- 根据选择从
pfArr
数组中获取对应函数的地址 - 调用该函数进行运算
- 打印结果
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 ret = 0; do { menu(); //函数指针数组的方式解决一下 //这里的函数指针数组,我们称为转移表 // 为什么这里要加NULL,直接{add,sub,mul,div}不行吗? 如果真是可以的话,那我们观察他的下标 0 1 2 3 此时此刻,如果我们选择1.add,那么(*p[1])取出的地址是sub,这不对呀, 如果我们在前面加一个NULL,{ NULL,add,sub,mul,div } 下标为 0 1 2 3 4 地址:*p[ 0 ] == 0, *p[ 1 ] ==add int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div }; // 0 1 2 3 4 printf("请选择:"); scanf("%d", &input); if (input == 0) { printf("退出计算器\n"); } else if (input >= 1 && input <= 4) { printf("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = pfArr[input](x, y); //(*p[input])==add/sub/mul/div函数名,也就是函数的地址 //(p[input])也可以,*号有无,都相当于函数名,也是函数地址 // 也就是ret=(p[input])(x,y); printf("%d\n", ret); } else { printf("选择错误,重新选择\n"); } } while (input); return 0; }
解释:
当input输入1, pfArr[1]取得Add的地址,然后通过Add函数的地址,执行指令。(当然同理input输入2,3,4也是同样的步骤)。
如果要增加功能,那么可以int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };增加相应的功能,然后增加相应功能的代码块!
比如,你想要增加位运算(&, |, ^)的功能:
解释:
当input输入1, pfArr[1]取得Add的地址,然后通过Add函数的地址,执行指令。(当然同理input输入2,3,4也是同样的步骤)。
如果要增加功能,那么可以int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };增加相应的功能,然后增加相应功能的代码块!
比如,你想要增加位运算(&, |, ^)的功能:
- 增加位运算函数:
nint And(int x, int y) { return x & y; } int Or(int x, int y) { return x | y; } int Xor(int x, int y) { return x ^ y; }
- 修改菜单显示:
void menu() { printf("******************************\n"); printf("**** 1. add 2. sub ****\n"); printf("**** 3. mul 4. div ****\n"); printf("**** 5. & 6. | ****\n"); printf("**** 7. ^ ****\n"); printf("**** 0. exit ****\n"); printf("******************************\n"); }
- 增加函数指针:
int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div, And, Or, Xor}; • 1
- 判断函数选择范围:
if(input >= 1 && input <= 7) { ret = pfArr[input](x, y); }
这样就增加了位运算的功能选择了。
如果还需要其他运算,可以继续增加对应的函数和菜单显示即可。
但是,思考———>
int (*p[5])(int x, int y) = { NULL,add,sub,mul,div };那函数的add,sub,mul,div这些地址是不是也和一维数组一样,存储在函数指针数组里面,他们的地址是否是连续的呢?
解释:
函数地址在函数指针数组中的存储方式与一维数组类似,但有一点不同:
- 函数指针数组
pfArr
中,add、sub等函数地址的存储是连续的,就像一维数组元素一样,如下标0,1,2,3,4这样连续存储后就可以访问了。 - 但是,函数本身的代码可能不一定存储在连续内存地址中。
举个例子:
假设add函数代码在地址0x00001000
,sub函数代码在0x00002000
,mul在0x00003000
。
那么在函数指针数组pfArr中:
fArr[1] 指向 add函数地址 0x00001000 pfArr[2] 指向 sub函数地址 0x00002000 pfArr[3] 指向 mul函数地址 0x00003000
我们可以看到,pfArr[1]、pfArr[2]、pfArr[3]中的函数地址是以连续的方式存储在数组中的。
但是函数本身的代码地址0x00001000、0x00002000、0x00003000并不连续。
所以总结来说:
函数指针数组pfArr中函数地址是连续存储的
但函数代码本身不一定连续存储在内存中
【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现3:https://developer.aliyun.com/article/1474742