我们在学习一门编程语言时,最为基本的就应该是排序的功能了,但是一旦要排序的类型发生变化(整型排序 ——> 字符串排序),我们就要重写一个类型的排序,这样子比较麻烦。
那么可不可以写一个通用的万能排序函数呢?
答案是肯定的,今天我们就来写一个万能的排序函数
#include
//冒泡排顺序函数
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 tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
//打印arr数组
void Print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = {
10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int sz = sizeof(arr) / sizeof(arr[0]); //数组的元素个数
//冒泡排序
bubble_sort(arr, sz);
//打印arr数组
Print(arr, sz);
return 0;
}
当我们要了解一个C语言函数,可以上 msdn 上搜索这个函数是怎么使用的。
C语言中的 qsort 函数为万能排序函数
通过了解 qsort 函数(万能排序函数),然后自己写一个万能排序函数
void qsort(void* base,
size_t num,
size_t width,
int(__cdecl* compare)(const void* elem1, const void* elem2));
void* base —— 表示要排序数对的第一个元素地址地址
num —— 表示排序的个数
witch —— 表示每一个要排序元素的大小
int(__cdecl* compare)(const void* elem1, const void* elem2)) —— 函数指针
compare —— 函数名
elem1 —— 函数的第一个参数
elem2 —— 函数的第二个参数
int —— 返回类型为整型
qsort 中的 compare 函数是由使用者来创建的。
那这个函数要怎样创建呢?
从上面这个图片中可以看到 compare 函数的返回值是什么。
当函数第一个参数小于第二个参数时,返回一个小于零的整数
当函数第一个参数大于第二个参数时,返回一个大于零的整数
当函数第一个参数等于第二个参数时,返回一个等于零的整数
void —— 无具体指针类型,
能够接收任意类型的地址,
缺点不能进行运算,不能加减整数,不能解引用。
qsort 函数中就是用 void 来接收的,那要怎样使用这个 void 类型呢?
使用强制转换之后,就可以使用 void 类型了。
排序整型
#include
#include
//打印
void Print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//自定义 qsort 函数中 cmp_int 函数
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
//e1为void*类型 —— 强制转换为int*类型
}
int main()
{
int arr[10] = {
9,8,7,6,5,4,3,2,1,0 };
//排序
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), cmp_int);
// arr —— 为int要排序元素的地址
// sizeof(arr) / sizeof(arr[0] —— 有多少个元素要排序
// sizeof(int) —— 排序类型的大小
// cmp_int —— 使用者自己创建的函数
//打印
Print(arr, sizeof(arr) / sizeof(arr[0]));
}
#include
#include
#include
//结构体
struct people
{
char number[20];
int age;
};
//qsort中的比较函数 —— 年龄比较
int cmp_by_age(const void* e1, const void* e2)
{
return ((struct people*)e1)->age - ((struct people*)e2)->age;
//((struct people*)e1 —— e1强制转换为(struct people*)类型
}
//打印
void Print1(struct people* p1, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%10s %4d\n", (p1+i)->number, (p1+i)->age );
}
}
//qsort中的比较函数 —— 名字比较
int cmp_by_numble(const void* e1, const void* e2)
{
return strcmp(((struct people*)e1)->number , ((struct people*)e2)->number);
// strcmp 可以比较两个字符串的大小
//((struct people*)e1 —— e1强制转换为(struct people*)类型
}
int main()
{
struct people p1[3] = {
{
"zhangsan", 30}, {
"lisi", 20}, {
"wangwu", 15} };
int sz = sizeof(p1) / sizeof(p1[0]); //sz要比较大小的元素个数
qsort(p1, sz, sizeof(struct people), cmp_by_age); //比较年龄 —— 升序
Print1(p1, sz); //打印
printf("=================================\n");
qsort(p1, sz, sizeof(struct people), cmp_by_numble); //比较名字 —— 升序
Print1(p1, sz); //打印
return 0;
}
#include
//交换
void swap_num(char* bulf1, char* bulf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
//交换字节 —— 每次循环交换一个字节
char tem = *(bulf1+i);
*(bulf1+i) = *(bulf2+i);
*(bulf2+i) = tem;
//如果widch大于1
//第一次循环交换一个字节,第二次交换下一个字节
}
}
//万能的冒泡排序
void bubble_sort(void* p1, size_t count, size_t width, int(*cmp)(const void* e1, const void* e2))
{
int i = 0;
//趟数
for (i = 0; i < count - 1; i++)
{
int j = 0;
//每趟的对数
for (j = 0; j < count - i - 1; j++)
{
if(cmp((char*)p1 + j * width, (char*)p1 + (j + 1) * width) > 0)
//当函数返回的值大于零时,才执行交换 ———— 升序
//(char*)p1 + j * width 表示:
//—— 第一次循环找到第一个元素地址,第二次循环找到第二个元素地址,...
//(char*)p1 + (j + 1) * width 表示:
//—— 第一次循环找到第二个元素地址,第二次循环找到第三个元素地址,...
//交换函数
swap_num((char*)p1 + j*width, (char*)p1 + (j+1)*width, width);
}
}
}
解析:
以整型为例:
if(cmp((char*)p1 + j * width, (char*)p1 + (j + 1) * width) > 0)
void swap_num(char* bulf1, char* bulf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
//交换字节 —— 每次循环交换一个字节
char tem = *(bulf1+i);
*(bulf1+i) = *(bulf2+i);
*(bulf2+i) = tem;
//如果widch大于1
//第一次循环交换一个字节,第二次交换下一个字节
}
}