C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针,指针进阶版的冒泡排序等介绍

文章目录

  • 前言
  • 一、函数指针的使用
    • 1. 加减乘除计算器普通实现
    • 2. 加减乘除计算机函数指针实现
  • 二、函数指针数组
    • 1. 函数指针数组的书写
    • 2. 两个有趣的代码
    • 3. 函数指针数组的使用
  • 三、指向函数指针数组的指针
  • 四、指针进阶_冒泡排序
    • 1.整型冒泡排序
    • 2. C语言qsort函数
    • 3. 仿写C语言qsort库函数
  • 总结


前言

C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍

一、函数指针的使用

1. 加减乘除计算器普通实现

#include <stdio.h>void menu()
{printf("***************************\n");printf("*****  1. Add  2. Sub******\n");printf("*****  3. Mul  4. Div******\n");printf("*****  0. Quit       ******\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();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出计算器\n");break;default :printf("输入错误\n");break;}} while (input);return 0;
}

2. 加减乘除计算机函数指针实现

  • 在上述基本实现中,每种情况(case)的代码大量冗余,可以使用函数指针来优化
  • 每种情况都调用 calc 函数,传入要进行运算的函数地址
  • 使用函数指针可以减少大量代码的冗余。
#include <stdio.h>void menu()
{printf("***************************\n");printf("*****  1. Add  2. Sub******\n");printf("*****  3. Mul  4. Div******\n");printf("*****  0. Quit       ******\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;
}
-------------------------------------------------------
void calc(int (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}
--------------------------------------------------------
int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){---------------------------------------------------case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;------------------------------------------------------case 0:printf("退出计算器\n");break;default:printf("输入错误\n");break;}} while (input);return 0;
}

二、函数指针数组

  • 存放函数指针的数组就是函数指针数组。

1. 函数指针数组的书写

// 函数指针的书写
int Add(int x, int y);
int (*pf)(int, int)  = &Add;
// 以上为函数指针的书写,函数指针数组的书写与函数指针可以通过函数指针的修改来得到
// 函数指针数组,顾名思义,一个数组的每个元素的类型都是函数指针。
int (*pfarr[5])(int, int) = { Add, Sub, Mul, Div};
// Add Sub Mul Div 均为函数名,即函数的地址
// pfarr[5] 是一个数组名为pfarr,数组元素个数为5 的数组
// 这个数组的元素类型是 int (*)(int, int),即函数指针类型。

2. 两个有趣的代码

( *(void(*)())0)();
  • 上述代码,void(*)() 是一个函数指针类型,带括号跟0,是将0强制转化为函数指针类型。
  • 最后再调用转换后的 0 地址对应的函数,没有参数。
void(*signal(int, void(*)(int)))(int);
  • 上述代码,本质上是一个函数的声明,函数名是 signal
  • signal函数的参数有两个,一个是 int 即 整型类型
  • 一个是 void(*)(int) 即 函数指针类型的,并且这个函数指针指向的函数参数有一个 int 即整型类型,并且返回值的类型是 void。
  • signal函数的返回值是 void(*)(int)即函数指针类型,也就是说它返回了一个函数的地址

上述 2 中的代码可以简化为更好理解的形式借助 typedef

typedef void (*pf_t)(int); // 这个语句将 void (*)(int) 类型重命名为 pf_t
// 所以 2 中的代码可以写成
pf_t signal(int ,pf_t);
// 这样就非常清晰了,signal 函数的返回值是 pf_t, 参数类型 为 int 和 pf_t

3. 函数指针数组的使用

  • 以 一、函数指针的使用中的计算器为例。
  • 使用函数指针数组,省略了switch分支语句的使用,更加节省了代码量
  • 最为关键的是,如果将来计算机需要增加功能,可以直接再函数指针数组中加入功能函数的地址即可。便于新增或减少计算器功能。
#include <stdio.h>void menu()
{printf("***************************\n");printf("*****  1. Add  2. Sub******\n");printf("*****  3. Mul  4. Div******\n");printf("*****  0. Quit       ******\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;do{menu();printf("请选择:>");scanf("%d", &input);----------------------------------------------------------------int (*pfarr[5])(int, int) = {0, Add, Sub, Mul, Div};int sz = sizeof(pfarr) / sizeof(pfarr[0]);if (0 == input){printf("退出游戏\n");break;}else if (input >= 1 && input < sz){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);int ret = pfarr[input](x, y);printf("%d\n", ret);}else{printf("输入错误\n");}----------------------------------------------------------------} while (input);return 0;
}

三、指向函数指针数组的指针

简单介绍

  • 通过函数指针数组的书写 -----> 指向函数指针数组的指针
int (*pfarr[5])(int, int) = {Add, Sub, Mul, Div};// 指向函数指针数组的指针,则需要取地址 函数指针数组
&pfarr;
// 用 *ppfarr来接收 指向函数指针数组的指针
// (*ppfarr)[5] 说明 指向函数指针数组的指针 所指向的数组有 5 个元素
// int (* (*ppfarr)[5])(int, int) 说明 指向函数指针数组的指针 所指向的数组元素的类型为 int(*)(int, int) 
// 即函数指针
int (* (*ppfarr)[5])(int, int) = &pfarr;

四、指针进阶_冒泡排序

1.整型冒泡排序

#include <stdio.h>void bubble_sort(int arr[], int sz)
{int i = 0;int j = 0;int tmp = 0;for (i = 0; i < sz - 1; i++){for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}int main()
{int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

2. C语言qsort函数

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
  • C语言中qsort函数的定义如上所示,
  • base 是 数组的起始地址,为空指针,可以接收任何类型的地址。
  • num 是 数组元素的个数。
  • width 是 一个数组元素所占字节大小。
  • compare指向一个比较函数,比较函数有两个参数,分别为要比较的两个数。
    注意:空指针是指没有具体类型指针,可以接受任何类型的指针,但是不能直接被解引用的

3. 仿写C语言qsort库函数

  • C语言的qsort函数可以比较任意类型的数据大小,并按照快速排序法进行排序。
  • 我们这里采用 冒泡排序仿写 qsort。
  • 函数名 bubble_sort
  • 函数参数 :
    1. 空指针 base 为了接收任何类型的数据
    1. sz 数组元素的大小
    1. width 一个数组元素所占字节大小
    1. *cmp 指向一个比较函数, 如:整形比较 cmp_int ,这个函数如果传入参数第一个大于第二个则返回大于零的数,如果相等,则返回0,如果小于,返回-1,可以直接用第一个数减去第二个数得到。
    1. swap 交换函数,传入两个值的地址,一个字节一个字节的进行交换。
  1. 整型冒泡排序
#include <stdio.h>
--------------------------------------------------------
// 比较整型的具体函数,它的调用者在bubble_sort中
int cmp_int(void* e1, void* e2)
{/*if (*(int*)e1 > *(int*)e2){return 1;}else if (*(int*)e1 == *(int*)e2){return 0;}else{return -1;}*/return (*(int*)e1 - *(int*)e2);
}
--------------------------------------------------------
// 交换函数 交换两个数的值,一个字节一个字节交换 它的调用者在bubble_sort中
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
--------------------------------------------------------
// 进行冒泡排序的逻辑
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){     // 调用比较函数Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);// 调用交换函数flag = 0;}}if (1 == flag){break;}}
}
--------------------------------------------------------
// 调用整型冒泡排序
void test1()
{int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
--------------------------------------------------------
//主函数
int main()
{test1(); // 整型排序//test2(); // 结构体中name字符的排序return 0;
}

执行结果如下:
在这里插入图片描述

  1. 结构体内字符串排序

#include <stdio.h>
-----------------------------------------------------------------
// 声明结构体类型
struct Stu
{char name[20];int age;
};
-----------------------------------------------------------------
// 结构体内字符串比较函数
int cmp_struct_by_name (void* e1, void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);// strcmp 函数返回的值 正好是 1 0 -1
}
-----------------------------------------------------------------
// 交换函数
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
-----------------------------------------------------------------
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);flag = 0;}}if (1 == flag){break;}}
}
-----------------------------------------------------------------
// 调用比较结构体内部的字符的排序
void test2()
{struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };int sz = sizeof(S) / sizeof(S[0]);bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_name);int i = 0;for (i = 0; i < sz; i++){printf("%s ", S[i].name);}
}
-----------------------------------------------------------------
int main()
{//test1(); // 整型排序test2(); // 结构体中name字符的排序return 0;
}

执行结果如下:
在这里插入图片描述

  1. 结构体内整型排序
#include <stdio.h>
--------------------------------------------------------------------
// 结构体类型声明
struct Stu
{char name[20];int age;
};
int cmp_struct_by_age(void* e1, void* e2)
{return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
--------------------------------------------------------------------
// 交换函数定义
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
--------------------------------------------------------------------
// 冒泡函数定义
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);flag = 0;}}if (1 == flag){break;}}
}
--------------------------------------------------------------------
//调用排序结构体类型中的整型
void test3()
{struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };int sz = sizeof(S) / sizeof(S[0]);bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_age);int i = 0;for (i = 0; i < sz; i++){printf("%d ", S[i].age);}
}
--------------------------------------------------------------------
int main()
{//test1(); // 整型排序//test2(); // 结构体中name字符的排序test3(); // 结构体中的age排序return 0;
}

执行结果如下:
在这里插入图片描述


总结

C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/4994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGLM2-6B的部署步骤_A3

ChatGLM2-6B 下载地址 一、VisualGLM-6B环境安装 1、硬件配置 操作系统&#xff1a;Ubuntu_64&#xff08;ubuntu22.04.3&#xff09; GPU&#xff1a;4050 显存&#xff1a;16G 2、配置环境 建议最好自己新建一个conda环境 conda create -n chatglm2 python3.8pip …

【Java】HOT100 贪心算法

目录 理论基础 一、简单贪心 LeetCode455&#xff1a;分发饼干 二、中等贪心 2.1 序列问题 LeetCode376&#xff1a;摆动序列 2.2 贪心股票问题 LeetCode121&#xff1a;买卖股票的最佳时机 LeetCode121&#xff1a;买卖股票的最佳时机ii 2.3 两个维度权衡问题 LeetCode135&…

UE Snap03 启动参数设置

UE Snap03 启动参数设置 UE打包后传入自定义参数及解析。 void UGameInstance::StartGameInstance() {Super::StartGameInstance();UE_LOG(LogTemp, Warning, TEXT("--StartGameInstance--"));FString param;FParse::Value(FCommandLine::Get(), TEXT("-UserN…

美团商城代付系统源码

超火的美团代付微信小程序是一种便捷的线上支付工具&#xff0c;让用户可以方便地在微信小程序中完成美团订单的支下面是将美团代付微信小程序源码搭建的相关步骤&#xff1a; 测试网站,页面放后面&#xff0c;可以定制哦 我收了几百块就帮我另外一个客户搭建好了 搭建教程: 以…

前端VUE项目中使用async()用法是为什么?能不用吗?

使用 async 关键字来定义一个函数主要有几个原因&#xff1a; 支持 await 关键字&#xff1a; async 函数允许你在其中使用 await 关键字&#xff0c;这使得你可以在不阻塞程序执行的情况下&#xff0c;等待一个异步操作&#xff08;如网络请求、文件读写等&#xff09;的完成。…

Nutch库入门指南:利用Java编写采集程序,快速抓取北京车展重点车型

概述 在2024年北京车展上&#xff0c;电动汽车成为全球关注的焦点之一。这一事件不仅吸引了全球汽车制造商的目光&#xff0c;也突显了中国市场在电动汽车领域的领先地位。117台全球首发车的亮相&#xff0c;其中包括30台跨国公司的全球首发车和41台概念车&#xff0c;彰显了中…

长难句打卡4.29

If appropriate public policies were in place to help all women—whether CEOs or their children’s caregivers—and all families, Sandberg would be no more newsworthy than any other highly capable person living in a more just society 如果能制定适当的公共政策…

张鸣独到政治观,规矩与自信新解

张鸣独解规矩与自信&#xff0c;社政新影响揭秘。张鸣独到政治观&#xff0c;规矩与自信新解在当今社会政治的大背景下&#xff0c;学者张鸣的每一次公开演讲无疑都是一次思想的盛宴。最近&#xff0c;他就当前的社会政治问题提出了自己独特的观点&#xff0c;特别是他对规矩和…

Acrobat Pro DC 2023:专业PDF编辑软件,引领高效办公新时代

Acrobat Pro DC 2023是一款专为Mac和Windows用户设计的专业PDF编辑软件&#xff0c;凭借其强大的功能和卓越的性能&#xff0c;成为现代职场人士不可或缺的得力助手。 这款软件拥有出色的PDF编辑能力。用户不仅可以轻松地对PDF文档中的文字、图片和布局进行编辑和调整&#xf…

Go 语言变量

变量来源于数学&#xff0c;是计算机语言中能储存计算结果或能表示值抽象概念。 变量可以通过变量名访问。 Go 语言变量名由字母、数字、下划线组成&#xff0c;其中首个字符不能为数字。 声明变量的一般形式是使用 var 关键字&#xff1a; var identifier type 可以一次声…

Debian 12 tomcat 9 日志信息 中文显示乱码

问题现象&#xff1a;Debian 12 linux操作系统中&#xff0c;tomcat 9 启动日志输出 中文乱码 解决办法&#xff1a; 1、先确保系统本身就支持中文的 Debian 系统支持中文可以通过 设置locale来实现 支持中文&#xff0c;需要使用zh_CN.UTF-8字符集 Debian 系统中 可以root用…

电力能源箱3D可视化:开启智慧能源管理新篇章

随着科技的不断进步&#xff0c;电力能源箱的管理与维护逐渐向着智能化、可视化的方向发展。3D可视化技术的崛起&#xff0c;不仅极大地提升了能源管理的效率&#xff0c;更以其直观、生动的特点&#xff0c;引领着电力能源管理领域迈入了一个全新的时代。 电力能源箱作为电力系…

Java8中的Stream流相关用法学习

目录 一、Stream是什么 二、创建Stream 三、中间操作 3.1 filter() 3.2 map() 3.3 flatMap() 3.4 distinct() 3.5 limit() 四、终端操作 4.1 findAny(), 和 orElse() 4.2 sorted() 4.3 forEach() 4.4 count() 4.5 collect() 4.6 groupingBy() 4.7 average() 4…

网络层 --- IP协议

目录 1. 前置性认识 2. IP协议 3. IP协议头格式 3.1. 4位版本 3.2. 4位首部长度 3.3. 8位服务类型 3.4. 16位总长度 3.5. 8位生存时间 TTL 3.6. 8位协议 3.7. 16位首部检验和 3.8. 32位源IP和32位目的IP 4. 分片问题 4.1. 为什么要分片 4.2. 分片是什么 4.2.1. …

Request processing failed: java.lang.IllegalArgumentException: Name for argument of type [java.lang

问题&#xff1a;使用spring-mvc进行获取前端参数时报错 具体报错如下&#xff1a; jakarta.servlet.ServletException: Request processing failed: java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name…

《ElementUI 基础知识》el-tabs header 监听鼠标中键滚动时左右滑动(ElementPlus同样适用)

前言 收到需求&#xff0c;可监听 el-tabs 头在鼠标 hover 时。滑动鼠标中键&#xff0c;可左右滑动&#xff01; 效果 鼠标中键上下滑动时&#xff1b;向上滑&#xff0c;向左移动&#xff1b;向下滑&#xff0c;向右移动&#xff1b; 实现 代码56 - 60行&#xff0c;添加…

Ubuntu TeamViewer安装与使用

TeamViewer是一款跨平台的专有应用程序&#xff0c;允许用户通过互联网连接从全球任何地方远程连接到工作站、传输文件以及召开在线会议。它适用于多种设备&#xff0c;例如个人电脑、智能手机和平板电脑。 TeamViewer可以派上用场&#xff0c;尤其是在排除交通不便或偏远地区…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(六)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 12 - 13节&#xff09; P12《11.ArkUI组件-循环控制》 forEach() 方法的使用方式&#xff1a; 在预览界面点击红框的按钮&#xf…

QT 开发COM(ActiveX)组件基础介绍和方案验证

一、COM简介 1.1 COM是什么&#xff1f; COM&#xff0c;Component Object Model&#xff0c;即组件对象模型&#xff0c;是一种以组件为发布单元的对象模型&#xff0c;这种模型使各软件组件可以用一种统一的方式进行交互。COM 既提供了组件之间进行交互的规范&#xff0c;也…

张大哥笔记:服务器有挖矿木马程序,该如何处理?

这篇文章发表于2021年&#xff0c;今天借这个平台再发布一下&#xff0c;希望对大家有所帮助&#xff01; 今天收到一个粉丝求助&#xff0c;说收到了阿里云官方短信通知提示有挖矿程序&#xff0c;要求立即整改&#xff0c;否则会关停服务器&#xff0c;以下是我和他的对话内…