✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
个人主页:@rivencode的个人主页
系列专栏:玩转C语言
推荐一款模拟面试、刷题神器,从基础到大厂面试题点击跳转刷题网站进行注册学习
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
通俗点就是你将一个函数的地址当做形参传给另外一个函数,另外一个函数既然要接收一个函数的地址则它的函数参数就为一个函数指针,然后通过函数指针去调用原来的函数,则称这个函数为回调函数。
总结:
qsort函数参数:
void* base:数组中第一个元素的地址,用void * 指针类型的指针接收
size_t num:数组的元素个数
size_t size:数组每个元素的大小(单位为字节)
int (*compar)(const void * e1,const void * e2) :比较数组两个元素的函数的地址,用函数的指针接收。
该函数的参数为:
e1:要比较的第一个元素的地址,用void *指针来接收
e2:要比较的第一个元素的地址,用void *指针来接收
该函数的返回值:为整形
当e1元素大于e2元素 返回1
当e1元素等于e2元素 返回0
当e1元素等于e2元素 返回-1
要用void *来接收元素的地址,因为要满足不同数据类型的元素比较即排序
typedef struct stu
{
char name[30];
int age;
}stu;
void Swap(char* buffer1, char *buffer2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buffer1;
*buffer1 = *buffer2;
*buffer2 = tmp;
buffer1++;
buffer2++;
}
}
int Cmp_Int(void *e1, void *e2)
{
return *(int *)e1 - *(int *)e2;
}
int Cmp_Float(void *e1, void *e2)
{
if (*(float*)e1 - *(float*)e2 > 0)
return 1;
else
return -1;
}
int Cmp_Stu_age(void *e1, void *e2)
{
return (((stu *)e1)->age - ((stu *)e2)->age);
}
int Cmp_Stu_name(void *e1, void *e2)
{
return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}
void Bubble_Sort(void*base, int num, int width, int(*cmp)(void *e1, 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);
}
}
}
}
void Test_int()
{
int i = 0;
int arr[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int num = sizeof(arr) / sizeof(arr[0]);
Bubble_Sort(arr, num, sizeof(arr[0]), Cmp_Int);
for (i = 0; i < num; i++)
{
printf("%d ",arr[i]);
}
}
void Test_float()
{
int i = 0;
float arr[10] = { 3.14, 2.5, 3.6, 7.8, 9.8, 6.6, 5.5, 4.4, 3.3, 2.2 };
int num = sizeof(arr) / sizeof(arr[0]);
Bubble_Sort(arr, num, sizeof(arr[0]), Cmp_Float);
for (i = 0; i < num; i++)
{
printf("%lf ", arr[i]);
}
}
void Test_stu_age()
{
stu s[3] = { { "c", 30 }, { "b", 20 }, {"a",10} };
int num = sizeof(s) / sizeof(s[0]);
Bubble_Sort(s, num, sizeof(s[0]), Cmp_Stu_age);
}
void Test_stu_name()
{
stu s[3] = { { "c", 30 }, { "b", 20 }, { "a", 10 } };
int num = sizeof(s) / sizeof(s[0]);
Bubble_Sort(s, num, sizeof(s[0]), Cmp_Stu_name);
}
int main()
{
//Test_int();
//Test_float();
//Test_struct();
//Test_stu_name();
return 0;
}
首先看一下二维数组元素在内存中如何存储
以前我们的思维可能是二维数组char a[3][4]是一个三行四列这样的布局,其实在内存中真正的是所有元素都是连续存储但为什么会出现这样的结果呢。
先说结论:
不管是二维还是三维还是多维数组都可以看做是’一维数组
’****,就拿二维数组来说char a[3][4],数组名与 [3] 结合说明数组有三个元素,而数组存储元素的类型是char [4],我们都知道char [4]是一个一维数组的类型,总结:char a[3][4] 是一个有3个元素的数组,而每个元素是一个一维数组。
数组类型 :去掉数组名剩余的部分
int arr[10]; ->类型为int[10]表示数组存储10个整形元素
char arr[10];->类型为char[10]表示数组存储10个字符元素
int*arr[10];->类型为int*[10]表示数组存储10个整形指针
int (*parr[10])(int ,int );->类型为int(*[5])(int ,int )
表示数组存储10个函数指针
数组中元素的类型:去掉数组名与[元素个数] 剩余的部分
int arr[10]; ->数组中元素的类型为int,数组存储10个整形
char arr[10];->数组中元素的类型为char,数组存储10个字符型
int*arr[10];->数组中元素的类型为 int *,数组存储10个整形指针
int (*parr[10])(int ,int );->数组中元素的类型为int (*)(int ,int ),数组存储10个函数指针
char a[3][4];-> 去掉 a[3],剩下的 char [4]的数组中存储的元素类型,而char [4]是一个存储4个字符的一维数组,则char a[3][4]有3个元素,每个元素是一个一维数组。
则就能解释为什么二维数组的元素存储都是连续,因为char a[3][4] 是一个数组,数组三个元素是连续存储,而每个元素是一个一维数组又是连续存储的,所以整个二维数组的元素都是连续存储的。
这里我画了个 char c[4][3] ->数组有4个元素,每个元素的类型是 char [3]是一个存储三个字符的一维数组。
三维数组
其实三维数组也可以看成是一维数组,char[2][4][3]数组有两个元素,只不过每一个元素的类型是 char[4][3]是一个二维数组,也就是说数组有两个元素每个元素是一个二维数组。
四维 ,五维…数组以此类推。
二维数组也是数组所以满足:
&arr-数组名代表整个数组,取出的是整个数组的地址
sizeof(arr)-数组名代表整个数组,计算的是整个数组的大小(字节)
先说结论:
1.让编译器不报警告
2.让程序员看增加代码可读性
3.对指针强制类型转化,会影响指针的解引用与指针运算的问题
4.强制类型转化后,后面所有运算问题按照强转后的新类型来计算
强制类型转化是改变的是对特定内容的看待方式,只会改变数据的类型,并不会改变数据在内存中存储的二进制序列。
*如果等号两边的数据类型不一样,编译器就会告警,此时我们将&a的类型从int * 强制转化为char 目的就是为了编译器不告警,且代码具有字描述性。
&arr-数组名代表整个数组,取出的是整个数组的地址
sizeof(arr)-数组名代表整个数组,计算的是整个数组的大小(字节)
const char * str=“abcdef” 与 char buffer[]="abcdef"的区别
先说结论:
字符串常量是存放在字符常量区与全局变量一样生命周期是从程序运行到结束,而且字符串常量是不可被修改。
char buffer[]=“abcdef” 字符串被保存在数组中, 而数组又是在栈上开辟(局部数组),函数运行完时数组被销毁字符串也被销毁,保存在数组中的字符串可以被修改。
二维数组
详细解析:
强制类型转化只会改变数据的类型不会改变其内容,改变看待数据的方式
详细解析: