
文章目录
这里是think的博客
希望可以一起交流知识,一起think
今天我们来学习冒泡排序和qsort
一起来think
冒泡排序
冒泡排序是一种排序思想,还有其他的排序方法,在学习数据结构的时候会详细讲解。
如果要排升序的话,冒泡其实就是将最大的数冒到最右边,怎么做到的?其实就是两两比较,大的移动到后面去,这样一趟冒泡排序就能将一个数冒到最后,我们知道如果有n个数就要冒n-1次,其中内部是冒了几个数,就要减少几次交换的次数,第一次冒的话,要交换n-1,第二次交换n-1-1次。
c
void Bubble_sort(int* arr, int n)
{
for (int i = 0; i < n - 1; i++)
{
int flag = 1;
for (int j = 0; j < n - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = 0;
}
}
if (flag == 1)break;
}
}
void PrintArr(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 9,1,4,3,5,7,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
PrintArr(arr, sz);
Bubble_sort(arr,sz);
PrintArr(arr,sz);
return 0;
}
回调函数
回调函数是什么?
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
我的理解是:我写好了一个函数,在传参的时候,我并没有立即执行它,而是把它的指针交给了另一个函数。另一个函数在处理完它自己的逻辑之后,'回过头来'调用了我传过去的这个函数,这就把之前本该完成的事情给补上了。
这种传参时不调用,处理完逻辑后再回过头来调用的机制,就叫做回调。"
回调函数的应用
c
//使⽤回到函数改造后
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void calc(int(*pf)(int, int))
{
int ret = 0;
int x, y;
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
int main()
{
int input = 1;
do
{
printf("************************
*\n");
printf(" 1:add2:sub \n");
printf(" 3:mul4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
回调函数的好处在于:传入函数指针的一方(传参方)知道自己要提供什么行为,而调用这个函数指针的一方(接收方)只约定接口,在真正接收到之前并不知道具体会是哪一个函数,这就是为什么calc可以复用。
->成员访问操作符
这个struct stu stus = {"zh",18}; (&stus)->age=20;,其中于.是点操作符,是针对结构体变量的,->是箭头操作符,是针对结构体指针的。
qsort的使用

c
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
void PrintArr(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int cmp_int(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
}
typedef struct stu stu;
struct stu
{
char name[20];
int age;
};
Print_stu_info(stu *ps,int n)
{
for (int i=0;i < n;i++)
{
printf("%s:%d\n", ps->name, ps->age);
ps++;
}
printf("\n");
}
void cmp_stu_name(const void* p1, const void* p2)
{
return strcmp((stu*)p1, (stu*)p2);
}
cmp_stu_age(const void* p1, const void* p2)
{
return ((stu*)p1)->age - ((stu*)p2)->age;
}
int main()
{
int arr[] = { 9,1,4,3,5,7,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
int len = sizeof(arr[0]);
PrintArr(arr, sz);
qsort(arr, sz, len, cmp_int);
PrintArr(arr, sz);
stu stus[3] = { {"zh",33},{"th",20},{"ah",88} };
int sz2 = sizeof(stus) / sizeof(stus[0]);
int len2 = sizeof(stus[0]);
Print_stu_info(stus, 3);
qsort(stus, sz2, len2, cmp_stu_name);
Print_stu_info(stus, 3);
Print_stu_info(stus, 3);
qsort(stus, sz2, len2, cmp_stu_age);
Print_stu_info(stus, 3);
return 0;
}
qsort的参数是base,首元素地址,num,元素个数和size,一个元素的大小,比较大小的函数。
如何理解记忆:int (*arr)[10],我们是先看中即*arr,,再看右边即10,元素个数,再看左边int的,一个元素的大小。
冒泡排序模拟实现qsort
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
void PrintArr(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int cmp_int(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
}
typedef struct stu stu;
struct stu
{
char name[20];
int age;
};
Print_stu_info(stu* ps, int n)
{
for (int i = 0; i < n; i++)
{
printf("%s:%d\n", ps->name, ps->age);
ps++;
}
printf("\n");
}
void cmp_stu_name(const void* p1, const void* p2)
{
return strcmp((stu*)p1, (stu*)p2);
}
cmp_stu_age(const void* p1, const void* p2)
{
return ((stu*)p1)->age - ((stu*)p2)->age;
}
void Swap(char* p1, char* p2, int len)
{
for (int i = 0; i < len; i++)
{
char temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2++;
}
}
void Bubble_sort2(void* Base, size_t sz, size_t len, int(*cmp)(const void* p1, const void* p2))
{
for (int i = 0; i < sz - 1; i++)
{
int flag = 1;
for (int j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)Base + j * len, (char*)Base + (j + 1) * len) > 0)
{
Swap((char*)Base + j * len, (char*)Base + (j + 1) * len, len);
flag = 0;
}
}
if (flag == 1)break;
}
}
int main()
{
int arr[] = { 9,1,4,3,5,7,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
int len = sizeof(arr[0]);
PrintArr(arr, sz);
Bubble_sort2(arr, sz, len, cmp_int);
PrintArr(arr, sz);
stu stus[3] = { {"zh",33},{"th",20},{"ah",88} };
int sz2 = sizeof(stus) / sizeof(stus[0]);
int len2 = sizeof(stus[0]);
Print_stu_info(stus, 3);
Bubble_sort2(stus, sz2, len2, cmp_stu_name);
Print_stu_info(stus, 3);
Print_stu_info(stus, 3);
Bubble_sort2(stus, sz2, len2, cmp_stu_age);
Print_stu_info(stus, 3);
return 0;
}
我们看到我们利用void*指针,同时利用回调函数实现了接收多个函数指针的功能进而实现了泛型编程。
其中void* Base const void* p1, const void* p2为什么前面是void*后面是const void*,这是因为Base指向的内容是要改变的,排序中的交换在这里就是一个字节一个字节的交换(因为不知道类型,所以只能用char这个最小单元来交换),所以你要改变就不能写const,但是其中的cmp函数的逻辑是不能改变值的大小,所以要加const,其实cmp函数内部通过强制类型转换也是可以改变大小的,但是如果加了const,改变了大小,编译器可能会报错,所以加const是为了告诉程序员不要再实现cmp函数的时候改变值的大小。
总结
谢谢观看!