目录
一:指针函数
(1)什么是函数指针?
函数指针:首先它是一个指针,一个指向函数的指针,在内存空间中存放的是函数的地址。
(2)函数名的意义。
我们都知道数组名在大部分情况下代表的是数组的首元素地址。&数组名代表的是数组的地址,尽管数值上一致,但代表的含义完全不同。
那我们可不可以认为函数名是函数所在空间的首地址,&函数名(函数指针)代表的是函数的地址呢?我们看下面这个例子。
我们可以看到二者的数值是相同的,那它们的意义是不是也如同数组名和&数组名一样呢?我们看下面对函数指针的引用。
按照我们原来对指针的理解,好像对p加*进行解引用才能找到函数,但我们可以看到函数名前面不管加几个*,结果都是一致的,由此我们得到结论:函数名与&函数名完全等价,不管函数名前面加多少个*都没有影响。
做个小结:函数名=&函数名
数组名!=&数组名
二:什么是qsort()函数
(1)qsort()函数的函数原型
void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *))
qsort(即,quicksort)主要根据你给的比较条件给一个快速排序,主要是通过指针移动实现排序功能。排序之后的结果仍然放在原来数组中。
qsort函数包含在头文件stdlib.h中。
(2):qsort()函数的参数意义
在讲参数意义之前,我们先说说,void类型的妙用,我们早期学习数组的时候传入的函数参数一般为数组对应类型的指针,这样只能接收一种数据类型,如果要修改会比较复杂,但是void类型可以接收任何类型,使代码更加的泛用。
但void类型是一个没有实际意义的类型,我们不能直接用void类型参与运算,也不能对void类型的指针解引用,除非进行强制类型转换。
第一个参数:void*用来接收待排序的目标数组的首元素地址。
第二个参数:num是待排序的目标数组的元素个数。
第三个参数:width是单个数组元素的大小(即字节数)
第四个参数:compare是一个函数指针 ,即用户自己定义的比较方法(函数)
三:冒泡排序
(1):什么是冒泡排序?
冒泡排序是一种比较简单的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换。
(2)图解:
四:回调函数
(1):什么是回调函数?
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
简单的讲,回调函数就是在特定条件下通过函数指针在另外一个函数的内部调用函数。
(2)回调函数的意义
我们可以看下面这个例子:
如果我们有四个函数,用来进行整形的加减乘除,我们在另外一个函数中需要这些功能,如果我们不使用函数指针,就需要写多个判断语句来进行选择,这样不够简洁,也不利于后续代码的维护。如果我们将函数指针作为参数传入,功能函数就可以通过修改参数来调用对应的函数,而它本身不用做任何的修改。
五:创建源文件和头文件
头文件:cmp.h用来包含一些必要的头文件,声明函数或者定义结构体。
源文件:cmp.c用来定义一些比较方法(函数)
bubble_sort.c用来定义主体函数bubbleSort(),进行相关的测试。
六:函数的具体实现
(1):函数bubblesort( )
void bubbleSort(void* s, int sz,int size,int(*cmp)(const void*e1,const void*e2))
bubblesort( )函数参数意义和qsort()函数一致
函数定义:
(2):比较方法(函数)
比较函数是由使用者自己定义的,也就是说使用者知道要比较的数据类型,我们需要接收数组两个元素的首地址,这个时候地址的类型是空类型,我们不能直接解引用,需要进行强制类型转换,如果第一个元素大于第二个元素,我们返回大于0的数,否则返回小于0的数。
int类型的比较方法:
char类型的比较方法:
结构体类型的比较方法(包含字符串类型的比较方法,注意包含头文件string.h):
(3)怎么找到函数元素的首地址?
函数内部已经传入了数组单个元素类型的大小,我们可以将传人的地址强制类型转换成(char*),这样我们就可以通过((char*)s + size*j)和((char*)s + size*(j+1))找到第j个和第j+1个元素的首地址,然后进行比较。
图解:
(4):怎么进行交换?
我们前面已经提到了char*强制转换,我们可以将元素的一个个字节进行交换,循环的条件由sz决定。
图解:
函数定义:
七:全部代码
(1)cmp.h
#include <stdio.h>
#include <string.h>
//字符比较
int cmp_char(const void* e1, const void* e2);
//整形比较
int cmp_int(const void* e1, const void* e2);
//结构体定义
struct stu {
char name[10];
int age;
};
//结构体内部成员比较
int cmp_stu_name(const void* e1, const void* e2);
//结构体内部成员比较(字符串比较)
int cmp_stu_age(const void* e1, const void* e2);
(2)cmp.c
#include "cmp.h"
int cmp_char(const void* e1, const void* e2)
{
return (*(char*)e1 - *(char*)e2);
}
int cmp_int(const void* e1, const void* e2)
{
return *((int*)e1) - *((int*)e2);
}
int cmp_stu_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int cmp_stu_age(const void* e1, const void* e2)
{
return (((struct stu*)e1)->age - ((struct stu*)e2)->age);
}
(3)bubble_sort.c
#include "cmp.h"
void swap(void* s1, void* s2,int size)
{
for (int i = 0; i < size; i++)
{
char c = 0;
c = *((char*)s1+i);
*((char*)s1+i) = *((char*)s2+i);
*((char*)s2+i) = c;
}
}
void bubbleSort(void* s, int sz,int size,int(*cmp)(const void*e1,const void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1 - i; j++)
{
//如果比较方法(函数)返回的值大于0,就进行交换
if (cmp(((char*)s + size*j), ((char*)s + size*(j+1)))>0)
{
//进行交换
swap(((char*)s + size * j), ((char*)s + size * (j + 1)),size);
}
}
}
}
//进行测试
void text1()
{
int a[7] = { 0,1,8,4,6,3,5 };
int sz = sizeof(a) / sizeof(a[0]);
bubbleSort(a, sz, sizeof(a[0]), cmp_int);
for (int i = 0; i < sz; i++)
printf("%d ", a[i]);
char str[4] = "aBDA";
int sz = sizeof(str) / sizeof(str[0]);
bubbleSort(str, sz, sizeof(str[0]), cmp_char);
for (int i = 0; i < sz; i++)
printf("%c ", str[i]);
struct stu s1[] = {
{"zhangsan",30},{"lisi",16} ,{"wangwu",26}};
int sz = sizeof(s1) / sizeof(s1[0]);
bubbleSort(s1, sz, sizeof(s1[0]), cmp_stu_name);
for (int i = 0; i < sz; i++)
printf("%s %d", s1[i].name, s1[i].age);
}
int main()
{
text1();
return 0;
}
八:结语
到此我们就完成了qsort()函数的模拟实现,大家也可以根据自己的需求设计更多比较方法(函数),相信各位对指针函数和回调函数有了更加清晰的认识,也希望各位能够评论指出我的不足,让我们共同进步,end!