前言
本小节,我们将继续学习C语言转移表,什么是回调函数,回调函数又是什么?qsort函数怎么使用,怎么理解处理,要注意的细节,当然qsort使用举例,最后我们进行qsort函数的模拟实现!文章干货满满,走起
一、转移表
C语言转移表是指根据一定条件,实现程序执行流程的跳转或转移的机制。
具体来说,C语言中实现转移表的主要方式有:
goto
语句:goto
语句可以实现无条件跳转,直接跳转到指定标签所在的代码块
goto 标签名; • 1
例如:
goto label;
switch
语句:switch
语句根据表达式的值,选择性地执行一个代码块。它实现了有条件跳转。
switch(表达式) { case 常数表达式1: //语句 break; case 常数表达式2: //语句 break; //其他case default: //语句 }
- continue语句:
continue
用于跳过循环体剩余部分,直接跳转到循环条件判断语句。
例如:
for(i=0;i<10;i++) { if(i==5) continue; printf("%d",i); }
break
语句:break
用于跳出整个循环或switch
语句。
例如:
for(i=0;i<10;i++) { if(i==5) break; printf("%d",i); }
return
语句:return
用于从函数中返回。
例如:
int func() { return 0; }
拓展:longjmp()/setjmp():
setjmp()和longjmp()是C语言中的两个非常重要的函数,它们可以实现非局部跳转的功能。
setjmp()函数声明如下:
int setjmp(jmp_buf env);
1
jmp_buf是可以保存环境信息的结构体。
setjmp()会将当前函数的执行环境信息保存到env中,并返回0。
然后程序可以正常执行。
当需要跳转时,调用longjmp(env, val);
longjmp()
函数声明如下:
void longjmp(jmp_buf env, int val);
longjmp()第一个参数就是setjmp()保存的env。
它会将程序跳转回setjmp()后面要执行的代码。
但此时setjmp()会返回longjmp()第二个参数val,而不是0。
jmp_buf env是setjmp和longjmp函数用来保存环境信息的结构体变量。
jmp_buf是一个预定义的数据类型,它用来描述一个环境的状态。
env是一个jmp_buf类型的变量。
当调用setjmp(env)时,setjmp函数会将当前函数调用栈(包括函数参数、局部变量等环境信息)保存到env这个结构体变量中。
之后程序可以正常执行。
当需要非局部跳转时,调用longjmp(env, val)。longjmp函数第一个参数就是这个env。
longjmp通过env这个结构体,可以恢复到setjmp函数保存环境时的状态。实现一个“跳回”的效果。
小总结:
jmp_buf
是一个结构体类型,它可以保存一个函数环境的状态信息。env
是一个此类型的变量,用于在setjmp
和longjmp
之间传递环境信息。setjmp
函数把当前环境信息保存到env
中。longjmp
函数通过env
这个结构体,实现恢复到setjmp
时的环境状态,从而实现非局部跳转。
哎!当然你可以把env可以看作是一个“传送令牌”,只要通过longjmp把令牌改了,他就重新传送到setjmp,然后继续执行,它连接setjmp和longjmp,使得longjmp能找到正确的环境信息进行跳转。
所以通过setjmp()/longjmp()就实现了一个非局部跳转:程序似乎"跳回"到setjmp()后面要执行的代码,但实际上环境已经发生了变化。这在异常处理等场景中很有用。
工作原理是:
setjmp()函数会保存当前函数调用栈(包括函数参数和局部变量等信息)的环境,并返回0。
之后程序可以正常执行。
当需要非局部跳转时,调用longjmp(),并将在setjmp()保存的环境作为参数传入。
这个时候程序就会跳转回setjmp()保存的环境,仿佛从setjmp()后面继续执行。但此时setjmp()会返回非0值。
setjmp()函数会保存当前函数调用栈(包括函数参数和局部变量等信息)的环境,并返回0。
之后程序可以正常执行。
举个例子
# define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <setjmp.h> jmp_buf env; //jmp_buf是一个预定义的数据类型,它用来描述一个环境的状态。 //env是一个jmp_buf类型的变量。 void func() { //设置跳转点 int ret = setjmp(env); if (0 == ret) { //正常流程 printf("In func()\n"); //触发跳转 longjmp(env, 1); } else { //跳转后流程 printf("Jumped back to func()\n"); } } int main() { func(); return 0; }
程序执行流程:
- 主函数调用
func()
函数。- func()内首先调用setjmp()设置跳转点env。由于setjmp()第一次调用会返回0,所以进入if块。
打印"In func()"信息。
调用longjmp(),触发非局部跳转。
程序跳转回setjmp()设置的环境env,此时setjmp()返回1。
执行else块内的代码,打印"Jumped back to func()"。
func()返回,主函数结束。
通过在函数内使用setjmp()/longjmp()
,实现了从函数内非局部跳回函数外的功能。这与goto
不同,可以实现跨函数的非顺序跳转。它常用于异常和错误处理等场景。
- C语言函数指针数组可以用来实现转移表。
具体来说:
- 定义一个函数指针数组,元素类型为函数指针。
- 每个数组元素都指向一个具体的函数。
- 根据条件调用数组对应元素所指向的函数。
这与传统的switch语句实现转移的效果是一致的。
个简单的示例:
// 函数定义 void func1() { printf("func1\n"); } void func2() { printf("func2\n"); } // 主函数 int main() { // 函数指针数组 void (*funcs[])(void) = { func1, func2 }; int id = 1; // 条件值 // 根据条件调用数组元素函数 funcs[id](); return 0; } void f
这样就实现了根据条件值动态调用不同函数的功能,相当于一个简单的转移表。
函数指针数组用于转移表的优点是:
- 更灵活,可以在运行时动态添加/删除函数
- 扩展性好,支持条件复杂情况下的多路径转移
- 与传统switch语句相比代码更简洁清晰
所以总的来说,函数指针数组正是C语言实现转移表的一个很好的选择。它可以很好地替代switch
语句实现更复杂的多路转移逻辑。
通过这个你可能不太能看出哪里能很好的替代switch
语句,让我们来看一个典型的例子,实现一个计算器!!!
【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现2:https://developer.aliyun.com/article/1474741