在学习使用函数qsort之前,先熟悉一下简单的冒泡排序。
int arr[10]={10,9,8,7,6,5,4,3,2,1}; 假设要使该数组中的内容由由小到大依次排序。
思路:
从第下标为0的第一个元素开始,与下一个元素进行比较,若该元素大于下一个元素,则交换,接着依次进行,第一个元素一直要比较到最后一个,一共有10个元素,所以需要10轮,在第一个元素进行比较时,需要比较10次,而第二个元素只需要比较8次即可,因为在第一轮循环中,已经把最大的元素交换到数组最后。所以需要两个for循环嵌套。
具体代码如下:
void Bubble_sort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] < arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
qsort函数是C语言的库函数之一,基于快速排序算法实现的一个库函数。在库stdlib.h中,在使用时要包含该库的头文件。
下面是qsort的部分说明:
void qsort(void *base ,size_t num,size_t width, int (*cmp)(const void*e1 ,const void*e2);
该函数没有返回值。
void* base 待排序位置的起始地址
num 是排序的个数
width 是排序元素的字节大小
int(*cmp)(const void*e1,const void*e2) 是一个函数指针,cmp为比较函数 e1 e2 待比较两个元素的地址,如果e1>e2,则返回一个>0的数,如果e1=e2则返回0,如果e1<e2,则返回一个<0的数。
1、qsort的用法
1、qsort实现冒泡排序
#include <stdio.h>
#include <stdlib.h>
void print(int* arr, int sz)
{
int i = 0;
for (i = 0; i <=sz - 1; i++)
{
printf("%d ", *(arr + i));
}
}
int cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2; // 默认为升序
}
int main()
{
int arr1[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr1) / sizeof(arr1[0]);
qsort(arr1, sz, sizeof(arr1[0]), cmp);
print(arr1, sz);
}
qsort的功能是非常强大的,这个int 类型是比较简单普遍的类型,也可以交换各种类型,如结构体类型。举例说明:
1、使结构体的内容按年龄排序。
//定义一个结构体
struct Stu
{
char name[20];
int age;
int score;
};
int cpm_age(const void* e1, const void* e2) // 比较年龄就写一个结构体年龄类型的比较
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{
struct Stu arr[3] = { {"zhangsan",20,55} ,{"lisi",25,56}, {"wangma",19,60}};
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cpm_age);
}
注意:
void* 这个类型的指针是万能的,兼容全部类型的指针,但是在用到该函数时,要将比较的元素强制类型转换为需要的类型,如比较整型就强制类型转换为整型,如果比较结构体类型,就强制类型转换为结构体类型。
2、使结构体的内容按名称排序。
不需要太大的改动,因为两个字符串之间是不能直接比较大小的,要用库函数中的strcmp函数比较。
strcmp函数是字符串比较函数,首先比较两个字符串的第一个字符,若这两个字符相等,则会继续比较下一个。
自己写一个函数,模拟实现qsort。
//用冒泡排序模拟实现qsort;
void Bubble_sort(void* base, int num, int width,int (*cmp)(const void* e1, const void* e2))
{
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);
}
}
}
}
在比较函数 cmp( (char*)base+j*width, (char*)base+(j+1)*width)中,里面的两个参数需要强制类型转换,因为void* 是空类型指针,没有实际意义,不能直接使用所以需要强制类型转换。在强制类型转换时,如果单一的强制类型转换为(int*)时,只能交换整型数据,兼容性很差。参考qsort函数,字符型、整型、自定义类型都可兼容。一个字符型指针变量能访问一个字节,一个短整型变量能访问两个字节,一个整型变量能访问4个字节。所以把数据强制类型转换为字节最小的,然后在累加,就可以达到类型所正常访问的字节。
如果base是一个整型,则base+1跳过四个字节,到下一个元素,但是base强制类型转换成char*类型的时候,base+1只能访问一个字节,要想访问整型的四个字节,只需base+1*width即可。
void swap(char* buf1,char* buf2,int width) //必须要知道元素的字节,如果是字符型,交换一个,如果是整型,则交换四个字节
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
在交换数据的时候,一个整型占四个字节,所以需要交换四个字节。
在写cap函数时,因为所需的变量是整型,所以只需将两个比较的类型强制转换为 (int*)即可
int cap_init(const void* e1, const void* e2)
{
return* (int*)e1 - * (int*)e2;
}
总代码为:
void swap(char* buf1,char* buf2,int width) //必须要知道元素的字节,如果是字符型,交换一个,如果是整型,则交换四个字节
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
//用冒泡排序模拟实现qsort;
void Bubble_sort(void* base, int num, int width,int (*cmp)(const void* e1, const void* e2))
{
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);
}
}
}
}
int cap_init(const void* e1, const void* e2)
{
return* (int*)e1 - * (int*)e2;
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_sort(arr, sz, 4, cap_init);
print(arr, sz);
return 0;
}