这一篇就是关于指针的最后一战了,主要是练习一个叫做 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 - a
return 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!指针的内容我也终于学完了,真是一场盛大得知识盛宴啊。
学习过程终归有些枯燥和乏味,但想起来最终目标还很远,所以还是要砥砺前行,感觉把自己说的有点高大上了,算了……一步步来。
指针到此结束,感谢各位的阅读!