大家好,这里是小bang子,今天给大家讲讲C语言编译器函数自带库自带的qsort排序函数。
目录
1.回调函数
1.1 qsort函数声明
1.2 qsort函数示例
1.3比较函数示例
2.模拟冒泡bubble
qsort函数中的比较函数就是一个回调函数,qsort包含在
void qsort( void *base,
size_t num,
size_t width,
int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
base——待排序数组首元素的地址
num——待排序数组的长度(元素个数)
width——待排序元素字节大小
compare——比较函数指针
compare的参数因为不知道具体数据类型,在设计的时候用void*来表示,使用时在compare函数内部根据数据类型进行强制转换比较大小。
Return Value | Description |
< 0 | elem1 less than elem2 |
0 | elem1 equivalent to elem2 |
> 0 | elem1 greater than elem2 |
#include
#include
//比较函数,根据需要自己建立
int cmp_int(const void* elem1,const void* elem2)
{
return (*(int*)elem1-*(int*)elem2);
}
//打印数组
void print_int_arr(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
}
int main()
{
int arr[10]={9,8,7,6,5,4,3,2,1,0};
int sz=sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_int);
print_int_arr(arr,sz);
return 0;
}
比较函数中第一个减第二个元素默认升序排序,若需降序只用把两个元素交换位置,用第二个元素减第一个元素,qsort函数中自带的有交换函数,我们只需要创建比较函数。
比较int类型
int cmp_int(const void* elem1,const void* elem2)
{
return (*(int*)elem1-*(int*)elem2);
}
比较结构体中关键字
struct stu
{
char name[10];
int age;
double score;
};
int cmp_stu_age(const void* e1, const void* e2)
{
//强制转换成结构体指针进行访问
return (((struct stu*)e1)->age-((struct stu*)e2)->age);
}
比较结构体中字符串,用
struct stu
{
char name[10];
int age;
double score;
};
int cmp_stu_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
qsort是基于快速排序的方法,我们这里模仿qsort的实现方法模拟个冒泡排序。
参数一致,采用冒泡排序
void bubble_sort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < num-1; i++)
{
int j = 0;
for (j=0;j < num - 1 - i; j++)
{
//比较2数大小读取字节方式跳过数
if (cmp_int((char*)base+j*width,(char*)base+(j+1)*width)>0)
{
//交换函数
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
//交换函数,char*读取一个字节,buf1存第一个数第一个字节的地址,buf2存第二个数第一个字节的地址
void swap(char*buf1,char*buf2,int width)
{
for (int i = 0; i < width; i++)
{
int tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
我们来讲解下这里面width的用法,以及base为啥要强制转换成char*类型。
首先数据在内存中是以字节存储的,1个int类型占4个字节,并且在vs编译器中是以小端字节序存储,如果不知道小端字节序存储可以查看下我发的一篇大小端字节序存储了解下https://mp.csdn.net/mp_blog/creation/editor/123193426
画图讲解下,假设待两个相邻的排序数字是4和5,在内存中如下存储
在冒泡中将base转换成char*类型,访问一个字节,然后通过数据字节大小width,实现指向下一个数据。
在交换函数swap中利用width实现将数据的每一个字节进行交换,达成交换数据的目的。
这样就实现了数据的交换。
示例:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
struct stu
{
char name[10];
int age;
double score;
};
int cmp_stu_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
//交换函数,char*读取一个字节,buf1存第一个数第一个字节的地址,buf2存第二个数第一个字节的地址
void swap(char*buf1,char*buf2,int width)
{
for (int i = 0; i < width; i++)
{
int tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void print_stu_name(struct stu* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s ", (arr + i)->name);
}
printf("\n");
}
void bubble_stu_name_sort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//比较2数大小读取字节方式跳过数
if (cmp_stu_name((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换函数
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
void test2()
{
struct stu arr2[3] = { {"zhangsan",19,88},{"lisi",15,70},{"langwu",24,68} };
int sz = sizeof(arr2) / sizeof(arr2[0]);
bubble_stu_name_sort(arr2, sz, sizeof(arr2[0]), cmp_stu_name);
print_stu_name(arr2, sz);
}
int main()
{
test2();
return 0;
}
以上就是这次qsort函数的分享,感谢大家的观看。