qsort函数的使用
一、qsort函数简介
qsort是C语言中的一个库函数,它是基于快速排序算法实现的一个排序函数,qsort函数最大的特点就是它可以排任意类型的数据。
其使用方法如下:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
其中,参数说明如下:
base:存放的是待排序数据的起始位置
size_t num:数组中元素的个数。
size_t width:每个元素的大小,以字节为单位。
compar:指向一个比较函数的指针,该函数接受两个参数,分别是指向要比较的元素的指针,返回值为整型,表示两个元素的大小关系。
elem1与elem2是待比较的两个元素的地址
如果elem1 less than elem2返回小于0的数字
如果elem1 equivalent to elem2返回等于0的数字
如果elem1 greater than elem2返回大于0的数字
注:
__cdecl *compare
是 qsort
函数的回调参数,它指向一个函数,这个函数被 qsort
多次调用,用于排序数组中的元素。排序算法需要比较数组元素的大小关系,而比较大小关系的准则是由应用程序自身提供的,因此在调用 qsort
函数时,需要向qsort
函数提供一个指向比较函数的函数指针。compare
便是这个指针,它指向一个用户自定义的比较函数。比较函数只需要实现比较操作即可,并返回一个整数值,表示大小关系。在使用 qsort
函数时,需要将一个数组指针,数组元素的个数,元素的大小和一个指向比较函数的指针作为参数传递给 qsort
函数。当 qsort
排序时,会多次调用指向 compare
函数的指针,用于实现元素的比较和排序,最终将数组排序后返回给调用者。在 qsort
函数中,void *base
参数表示要排序的数组的首元素地址。具体来说,它是一个指向待排序数组的指针。该指针指向数组的第一个元素,qsort
函数通过该指针访问整个数组的元素。这个指针的类型为 void*
,这意味着它可以指向任何类型的数据,同时也说明了 qsort
函数是一个通用的排序函数,可以对任何类型的数据进行排序。
base
本身并不包含任何有关数组大小和元素类型的信息,这些信息需要通过其他参数(如 size_t nmemb
和 size_t size
)来提供。在使用 qsort
函数时,需要确保 base
指针指向包含足够元素数目的有效数组,否则可能会引起越界访问错误或未定义行为。
在 qsort
函数中,const void *elem1
参数是一个指向数组中某一个元素的指针,它表示待排序的第一个元素。elem1
参数的类型为 const void*
,它被声明为常量指针,这意味着函数不能修改指针所指向的值。这是因为,在排序过程中,qsort
函数不会更改数组的值,而只是通过调整元素的顺序来使数组变得有序。elem1
参数是由 qsort
函数以及比较函数自动传递给程序的,作为比较函数的输入,比较函数使用 elem1
和 elem2
来比较数组元素的大小。需要注意的是,由于 elem1
参数被声明为常量指针,意味着在比较函数中不能修改它所指向的值。如果在比较函数中需要修改数组元素的值,则可以通过用类型转换来去掉 const
限定符,例如 (void*)elem1
可以将 const void*
类型转换为 void*
类型,这样指针所指向的值就可以修改了。但需要谨慎使用,必须确保修改指针所指向的值不会影响程序的正确性。
elem2
参数的类型为 const void*
,它被声明为常量指针,这意味着函数不能修改指针所指向的值。这是因为,在排序过程中,qsort
函数不会更改数组的值,而只是通过调整元素的顺序来使数组变得有序。elem2
参数是通过比较函数来定义的,该函数的作用是比较两个元素的大小。因此,elem2
参数实际上是由 qsort
函数以及比较函数自动传递给程序的。需要注意的是,由于 elem2
参数被声明为常量指针,因此如果在比较函数中需要修改数组元素的值,则不应该使用 const
关键字来声明参数。
二、qsort函数用法
1.qsort函数对整型数组排列
下面是一个示例程序,展示如何使用qsort函数对整数数组进行升序排列:
典例1:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
void printf_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int cmp_int(const void* e1, const void* e2)
{
//排序为升序
return (*(int*)e1 - *(int*)e2); //*(int*)e1表示将e1强制类型转化为int*,return在这里返回大于0小于0或等于0的数
}
void test1()
{
int arr[] = { 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); //用qsort排整型
printf_arr(arr, sz);
}
int main()
{
test1();
return 0;
}
运行结果:
典例2 :
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
int compare(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int main() {
int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
在这个示例程序中,我们定义了一个名为compare的比较函数,用于指定排序的方式。在主函数中,我们声明了一个整数数组,调用了qsort函数对其进行排序,并打印了排序后的结果。
2.qsort函数对结构体排列
下面是一个示例程序,展示如何使用qsort函数对整数数组进行升序排列:
典例1:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
double score;
};
int com_stu_by_age(const void* elem1, const void* elem2)
{
//升序打印
return ((struct Stu*)elem1)->age - ((struct Stu*)elem2)->age;
}
int com_stu_by_name(const void* elem1, const void* elem2)
{
return strcmp(((struct Stu*)elem1)->name , ((struct Stu*)elem2)->name);
//升序打印
//stringcmpare是按照字典的顺序来比较的,两个字符在比较的时候,比较的是它的ascll码值
}
void test1()
{
struct Stu arr[3] = { {"zhangsan",25,80.0},{"lisi",35,88.0},{"wangwu",22,96.0} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), com_stu_by_age);
//qsort排序年龄打印结果22,25,35
//qsort(arr, sz, sizeof(arr[0]), com_stu_by_name);
//qsort排序年龄打印结果lisi,wangwu,zhangsan
}
int main()
{
test1();
return 0;
}
【注意】想要将升序改为降序,只需要将elem1与elem2的位置进行调换就行。
年龄调试结果:
姓名调试结果:
三、以qsort思想升级冒泡排序
1.冒泡排序整型数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int cmp(const void* elem1, const void* elem2)
{
return (*(int*)elem1 - *(int*)elem2);
}
void Swap(char*buf1, char*buf2,int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void*base, int num, int width, int(*cmp)( const void*elem1, const void*elem2 ))
{
//计算行
int i = 0;
for (i = 0; i <num - 1; i++) //计算冒泡排序的趟数
{
//计算列
int j = 0;
for (j = 0; j < num-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);
}
}
}
}
void print_sort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test2()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
//排序为升序
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp);
print_sort(arr, sz);
}
int main()
{
test2();
return 0;
}
运行结果:
2.冒泡排序结构体
排序姓名:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
double score;
};
int cmp_stu_by_name(const void* elem1, const void* elem2)
{
return strcmp(((struct Stu*)elem1)->name , ((struct Stu*)elem2)->name);
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, int num, int width, int(*cmp)(const void* elem1, const void* elem2))
{
//计算行
int i = 0;
for (i = 0; i < num - 1; i++) //计算冒泡排序的趟数
{
//计算列
int j = 0;
for (j = 0; j < num - 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);
}
}
}
}
void test2()
{
struct Stu arr[3] = { {"zhangsan",20,55.5},{"lisi",30,88.0},{"wangwu",10,90.0}};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
test2();
return 0;
}