C语言学习记录--深入理解指针(5)(qsort的练习)
这一篇就是关于指针的最后一战了,主要是练习一个叫做 qsort 的C语言库函数
正文开始:
一.什么是qsort函数?
qsort 就是 quick sort,也就是“快速排序”,意思就是说qsort就是一个排序函数,和之前写的冒泡排序不同,冒泡排序只能排序整型数据,而qsort可以排序任意类型的数据。
注意:使用qosrt需要包含头文件 #include<stdlib.h>
下面是我在cpulspuls 网站截到的图片,用来作qsort的说明:
可以看到,这个函数的使用需要传递4个参数,分别是:
1.要排序的数组的首元素指针(即数组名)。
2.待排序数组中的元素个数。(可以用 sizeof(arr)/sizeof(arr[0]) )
3.待排序数组中每个元素的字节大小(即 sizeof(arr[0] )
4.比较两个元素大小比较的函数的指针(即函数名)
前面三个参数都很好理解,关键在于第四个,这是一个比较数组中元素大小的函数,根据这个函数的返回值决定元素的相对顺序。
二.qsort的应用
代码格式如下:
#include<stdlib.h>
void compare(const void* p1, const void* p2)
{}
int main()
{int arr[10] = { 9,0,5,7,3,2,4,1,8,6 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), compare);return 0;
}
然后来写函数:
void compare(const void* p1, const void* p2)
{if (*(int*)p1 > *(int*)p2)//因为void*类型不能解引用,所以这里使用(int*)来强制类型转换。{return 1;}else if (*(int*)p1 < *(int*)p2){return -1; }else{return 0;}
}
返回大于0的数(1)和小于0的数(-1)来比较元素大小,
完整代码效果如下:
#include<stdlib.h>
void compare(const void* p1, const void* p2)
{if (*(int*)p1 > *(int*)p2){return 1;}else if (*(int*)p1 < *(int*)p2){return -1; }else{return 0;}
}void print(int* arr, int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", *(arr + i));}printf("\n");
}int main()
{int arr[10] = { 9,0,5,7,3,2,4,1,8,6 };int sz = sizeof(arr) / sizeof(arr[0]);printf("排序前:");print(arr, sz);qsort(arr, sz, sizeof(arr[0]), compare);printf("排序后:");print(arr, sz);return 0;
}
三.qosrt的其他应用举例
1. 字符串排序(按字典序)
在处理文本数据时,我们经常需要对字符串进行排序,比如将单词按字典序排列。以下是使用qsort函数实现字符串排序的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 字符串比较函数(用于qsort)
int compare_strings(const void *a, const void *b) {// 将void指针转换为char**,再解引用得到char*return strcmp(*(const char **)a, *(const char **)b);
}int main() {const char *strings[] = {"banana", "apple", "cherry", "date", "elderberry"};int n = sizeof(strings) / sizeof(strings[0]);// 排序字符串数组qsort(strings, n, sizeof(const char *), compare_strings);// 输出排序结果printf("排序后的字符串:\n");for (int i = 0; i < n; i++) {printf("%s\n", strings[i]);}return 0;
}
运行结果:
2. 结构体排序(按学生成绩降序)
当需要对自定义结构体数据进行排序时,qsort同样能发挥作用。假设我们有一个学生结构体,包含姓名和成绩信息,现在要按成绩从高到低对学生进行排序,代码如下:
#include <stdio.h>
#include <stdlib.h>// 定义学生结构体
typedef struct {char name[50];int score;
} Student;// 学生比较函数(按成绩降序)
int compare_students(const void *a, const void *b) {// 将void指针转换为Student*const Student *student_a = (const Student *)a;const Student *student_b = (const Student *)b;// 降序排序:b - areturn student_b->score - student_a->score;
}int main() {Student students[] = {{"Alice", 85},{"Bob", 92},{"Charlie", 78},{"David", 92},{"Eve", 88}};int n = sizeof(students) / sizeof(students[0]);// 排序学生数组qsort(students, n, sizeof(Student), compare_students);// 输出排序结果printf("按成绩降序排列的学生:\n");for (int i = 0; i < n; i++) {printf("%s: %d分\n", students[i].name, students[i].score);}return 0;
}
运行结果:
四.使用冒泡排序的底层逻辑模拟qsort
当我们学会了冒泡排序后,发现它只能排序整型数据,那么有办法让冒泡排序算法(Bubble_sort)也能够排序任意类型的数据吗?
有的兄弟,有的
接下来就是本篇的重点了,即使用冒泡排序的底层逻辑模拟qsort。
我这里直接先把代码端上来吧:
#include<stdio.h>
void swap(char* bf1, char* bf2, size_t width)
{int i = 0;char tmp = 0;for (i = 0;i < width;i++){tmp = *bf1;*bf1 = *bf2;*bf2 = tmp;bf1++;bf2++;}
}int cmp(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}void print(int arr[10], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", *(arr + i));}printf("\n");
}void bubble_sort(void* base, size_t sz, size_t width, int(*cmp)(const void* p1, const void* p2))
{int i = 0;for (i = 0;i < sz - 1;i++){int j = 0;for (j = 0;j < sz - 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 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, sizeof(arr[0]), cmp);print(arr, sz);return 0;
}
这段代码实现了一个通用的冒泡排序算法,它能够对任意类型的数据进行排序,具体功能包括:
- 内存交换函数:
swap
函数用于交换两个内存块的内容 - 比较函数:
cmp
函数用于定义元素之间的比较规则 - 打印函数:
print
函数用于输出排序结果 - 通用冒泡排序:
bubble_sort
函数实现核心排序逻辑 - 主函数:演示如何使用上述功能对整数数组进行排序
我想写更清楚一点儿的解释,可奈何有点力不从心(说白了现在还没有能力用文字把所有代码解释清楚)
主要难点在于这两个地方:
- 内存交换函数:
swap
函数用于交换两个内存块的内容 - 比较函数:
cmp
函数用于定义元素之间的比较规则
swap
函数是实现元素交换的核心:
void swap(char* bf1, char* bf2, size_t width)
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *bf1;*bf1 = *bf2;*bf2 = tmp;bf1++;bf2++;}
}
这个函数通过逐字节交换两个内存块的内容来实现元素交换。由于使用了char*
指针,每次只能访问一个字节,因此需要循环width
次,确保交换整个元素的内容。
比较函数cmp
是定义排序规则的关键:
int cmp(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}
比较函数的设计遵循以下原则:
- 接受两个
const void*
类型的参数 - 将参数转换为实际数据类型的指针
- 根据比较结果返回负值、零或正值
其他的点在熟悉C语言指针的情况下是很容易理解的。
OK!指针的内容我也终于学完了,真是一场盛大得知识盛宴啊。
学习过程终归有些枯燥和乏味,但想起来最终目标还很远,所以还是要砥砺前行,感觉把自己说的有点高大上了,算了……一步步来。
指针到此结束,感谢各位的阅读!