了解更多知识请点我:学习C语言之路(汇总篇)
手把手教你使用回调函数(冒泡排序算法举例)
总体思路
- 字节交换
- 比较函数
- 冒泡算法
冒泡函数基本要素
1.字节交换函数
void byte_swap(void *pData1, void *pData2, size_t stSize)
{
unsigned char *pcData1 = pData1;
unsigned char *pcData2 = pData2;
unsigned char ucTemp;
while (stSize--){
ucTemp = *pcData1; *pcData1 = *pcData2; *pcData2 = ucTemp;
pcData1++; pcData2++;
}
}
- 关于字节交换函数请参考该链接:用C写第一个交换函数swap&&升级版
2.3种比较函数
int compare_int(const void * e1, const void * e2) //大
{
return *(int *)e1 - *(int *)e2;
}
int compare_int_r(const void * e1, const void * e2) //小
{
return *(int *)e2 - *(int *)e1 ;
}
int compare_str(const void * e1, const void *e2) //比较字符串大小
{
return strcmp(*(char **)e1, *(char **)e2);
}
-
说明:由于我们这边定义的类型是void * 所有我们首先将(int )e1.ps:e1先强制转化为指向int 类型的指指针。然后在使用取该地址的值 retrun 输入两个参数的比较值,如果值为正数,说明e1 大于e2 ,反之则小于。
还有一个疑问为什么要加const呢???
忘了说了 哈哈哈 这边加const 相当于修饰一个常量的指针,修饰的内容在函数中无法进行修改。
那什么是常量呢???
举例
int a[2] = {0x31,0x32};
const int *c =a ; // 这是合法的,
非法的是对c的使用
*c =2 ; // 非法,但可以这样修改c指向的对象的值:a[0] = 2; !!! 错误 常量无法修改 -
还有字符串的比较,(char **) 这边为什么会有两个 **,额…忘了说了
我这边后面定义的是一个char * arrayStr[] = { “Sunday”,“Monday”,“Tuesday”,“Wednesday”,“Thursday”,“Friday”,“Saturday” };
因为arrayStr[0]的类型是的指向char * 的地址,不是“Sunday”,那我这边怎么取“Sunday”地址呢,&arrayStr[0] 就是再取一次地址 这次的数据类型就相当于char ** arryStr取两次地址。
3.冒泡算法函数
void bubbleSort(void * base, size_t nmemb, size_t size, COMPARE compare)
{
int hasSwap=1;
int i;
int j;
for ( i = 0; hasSwap&&i < nmemb - 1; i++) // 大循环
{
hasSwap = 0; //判断条件是否成立
for ( j = 0; j < nmemb - 1-i; j++) //小循环
{
void *pThis = ((unsigned char *)base) + size*j;
void *pNext = ((unsigned char *)base) + size*(j+1);
if (compare(pThis, pNext) > 0)
{
hasSwap = 1; //
byte_swap(pThis, pNext, size);
}
}
}
来来来 ,相比大家上面看有点晕,哈哈,那就再来点猛料!!!(不是一家人,不进一家门)
参数介绍
-
参数一:void * base,这边这边是要接受所有类型的数据,所以这边第一个参数要为 void *类型 ,允许支持不同类型的数据比较排序。
-
参数二:很关键,就是比较的数据的总个数,举例int arrayInt[] = { 39, 33, 18, 64, 73, 30, 49, 51, 81 };那这个时候,我们可以使用sizeof(arrayInt)/ sizeof(arrayInt[0])
(ps :sizeof实际上是获取了数据在内存中所占用的存储空间,以字节为单位来计数。)
举例
int a=10; int arr=[1,2,3]; sizeof(a) = 4; sizeof(arr) = 12 ; -
参数三:每元素的数据类型 可以使用sizeof(arr[0]);
-
参数四:回调函数,也是该函数最重要的设计点,它的数据类型为COMPARE (int (*)(const void * e1, const void * e2))。
逻辑介绍
先看第一个for循环,定义了一个hasSwap标志位作为判断有什么用意呢? 后面我们再讲。进入第二个for,这个for循环总共巡行(nmenb-1-i)次(原因在于n个数据对比,只要对比n-1次就好了 ,吧不信你自己试),这段代码最核心的函数为compare函数,它决定了排序的走势,假如它是升序,那在这次循环后,最大的一位将排序最后,
举例定义一个数组
- 小循环执行一次后 ,执行次数为8次,将最大的数据81排到了最后一位
- int arrayInt[] = { 39, 33, 18, 64, 73, 30, 49, 51, 81 };
- hasSwap = 1; 再执行大循环 ,i =1
- 小循环第二次,执行次数为7次,将73数据排到倒数第二位
- int arrayInt[] = { 39, 33, 18, 64,30, 49, 51, 73,81 };
- …
- 如此循环
全部代码
#include<stdio.h>
#include<string.h>
#include<stddef.h>
typedef int(*COMPARE)(const void * e1, const void *e2);
void byte_swap(void *pData1, void *pData2, size_t stSize)
{
unsigned char *pcData1 = pData1;
unsigned char *pcData2 = pData2;
unsigned char ucTemp;
while (stSize--){
ucTemp = *pcData1; *pcData1 = *pcData2; *pcData2 = ucTemp;
pcData1++; pcData2++;
}
}
// bubbleSort(arrayInt, numArray, sizeof(arrayInt[0]), compare_int);
void bubbleSort(void * base, size_t nmemb, size_t size, COMPARE compare)
{
int hasSwap=1;
int i;
int j;
for ( i = 0; hasSwap&&i < nmemb - 1; i++) //带入循环9次
{
hasSwap = 0; //判断条件是否成立
for ( j = 0; j < nmemb - 1-i; j++)
{
void *pThis = ((unsigned char *)base) + size*j;
void *pNext = ((unsigned char *)base) + size*(j+1);
if (compare(pThis, pNext) > 0)
{
hasSwap = 1; //
byte_swap(pThis, pNext, size);
}
}
}
}
int compare_int(const void * e1, const void * e2) //大
{
return *(int *)e1 - *(int *)e2;
}
int compare_int_r(const void * e1, const void * e2) //小
{
return *(int *)e2 - *(int *)e1 ;
}
int compare_str(const void * e1, const void *e2) //比较字符串大小
{
return strcmp(*(char **)e1, *(char **)e2);
}
void main()
{
int arrayInt[] = { 39, 33, 18, 64, 73, 30, 49, 51, 81 };
int numArray = sizeof(arrayInt) / sizeof(arrayInt[0]);
int i;
////// 从小到大
bubbleSort(arrayInt, numArray, sizeof(arrayInt[0]), compare_int);
for ( i = 0; i <numArray; i++) {
printf("%d ", arrayInt[i]);
}
printf("\n");
////// 从大到小
bubbleSort(arrayInt, numArray, sizeof(arrayInt[0]), compare_int_r);
for ( i = 0; i <numArray; i++) {
printf("%d ", arrayInt[i]);
}
printf("\n");
char * arrayStr[] = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" };
numArray = sizeof(arrayStr) / sizeof(arrayStr[0]);
bubbleSort(arrayStr, numArray, sizeof(arrayStr[0]), compare_str);
for ( i = 0; i < numArray; i++)
{
printf("%s\n", arrayStr[i]);
}
}
调试结果
重点来了–回调函数—介绍
-
上层模块为mian() 和compara_int
-
下层模块为bubbleSort () 。
-
链路说明如下:上层mian()将(arrayStr, numArray, sizeof(arrayStr[0]), compare_str)等参数给bubbleSor() 。bubbleSor()回调compara_int ()函数,达到上层A调用下层B,B执行的过程中又将信息返回给上层模块。而对于上层模块,上层模块compara_int 不仅监视下层bubbleSor() ,还干预了bubbleSor()的运行,本职上是上层模块调用下层模块实现是分层的思想
设计分层思想总结
- 分层设计:就是将软件按照某种上级级的关系进行分层,每隔层之间通过API接口进行调用。
– 硬件驱动层:顾名思义,就是和硬件打交道最密切的部分,举例:显示器、蜂鸣器等
– 虚拟层:它是根据需求进行划分
– 应用层:直接用于功能实现。人机交互。
基于分层架构有一下优点:1降低系统复杂度,2隔离变化,3有利于测试,4可移植性。
介绍一个原则:好莱坞原则(Hollywood),下层不能调用上层的函数,层和层之间不能循环调用。不要调用我,让我调用你