文章目录
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
举个简单的例子供大家理解一下
上代码
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("**************************\n");
printf("**1:add************2:sub**\n");
printf("**3:mul************4:div**\n");
printf("**************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
这里有一个很明显的问题,冗余:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
这个部分如果可以用回调函数(使用函数指针)的方式实现的话,可以大大减少冗余的情况。
改:
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void Caculate(int(*pf)(int, int))
{
int x, y;
int ret = 0;
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);//使用函数指针,将add,sub,mul,div用指针pf替代
printf("ret = %d\n", ret);
}
int main()
{
int input = 1;
do
{
printf("**************************\n");
printf("**1:add************2:sub**\n");
printf("**3:mul************4:div**\n");
printf("**************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
Caculate(add);
break;
case 2:
Caculate(sub);
break;
case 3:
Caculate(mul);
break;
case 4:
Caculate(div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
有一个比较典型的排序函数:qsort函数,它就用到了回调函数,也就说里面的参数是有函数指针的。
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
它共有四个参数
base:指向要排序的数组的第一个对象的指针,转换为void*。
num:由基指向的数组中的元素数。size_t是无符号整数类型。
size:数组中每个元素的字节大小。size_t是无符号整数类型。
compar:指向比较两个元素的函数的指针。qsort反复调用这个函数来比较两个元素。
应遵循以下原型:int compar(const void* p1,const void * p2);
具体的函数信息比如上述所描述的int compar(const void* p1,const void * p2)
可以通过一个网址进行查询cplusplus.com,非常的好用,力荐。
使用qsort的例子:
#include <stdio.h> /* printf */
#include <stdlib.h> /* qsort */
int values[] = { 40, 10, 100, 90, 20, 25 };
int compare (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int main ()
{
int n;
qsort (values, 6, sizeof(int), compare);
for (n=0; n<6; n++)
printf ("%d ",values[n]);
return 0;
}
输出结果为10 20 25 40 90 100
当然既然我们要好好学学一门语言,那么我们可以试着自己来造轮子来完成qsort函数的功能。
如下
先了解一下void* 型的指针
1.可以接收任意数据类型的地址
2.void* 的指针不能直接+- 整数的操作
3.void* 的指针不能直接解引用操作
另外bubble_sort,冒泡排序的原理大家应该需要明白。
#include <stdio.h>
#include <stdlib.h>
void Swap(int width, char* bigarr, char* smallarr)
{
int i = 0;
for (i = 0; i < width; i++)
{
int tmp = *(bigarr + i);
*(bigarr + i) = *(smallarr + i);
*(smallarr + i) = tmp;
}
}
int Cmp_Int(const void* e1, const void* e2)
{
return *(int *)e1 - *(int *)e2;
}
void Bubble_Qsort(void* arr,
int sz, int width,
int(*Compare)(const void*, const void*))
//int(*Compare)(const void*, const void*)为函数指针,用它来指向Cmp_Int的地址,并且在满足条件的情况下调用它
{
int i = 0;
int j = 0;
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1 - i; j++)
{
//使用函数指针调用函数
if (Compare((char*)arr + j*width,
(char*)arr + (j + 1)*width)>0)//升序
{
//交换每个字节
Swap(width, (char*)arr + j*width, (char*)arr + (j + 1)*width);
}
}
}
}
int main()
{
int i = 0;
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_Qsort(arr, sz, sizeof(int), Cmp_Int);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
在这里面调用的 Cmp_Int 就被称之为回调函数,希望小伙伴们认真梳理一下其中的逻辑关系。
当然了如果你想比较结构体类型,或者是其他的类型,把int Cmp_Int(const void* e1, const void* e2)稍作改动即可,
例如结构体类型,例子为普通人的基本信息,并按照年龄排序(升序)
struct PeoInfo
{
char name[20];
int age;
char sex[5];
};
Cmp_PeoInfo_By_Age(const void* e1, const void* e2)
{
return (*(struct PeoInfo*)e1).age - (*(struct PeoInfo*)e2).age;
}
其实只要大家好好学,然后离开代码自己理一理逻辑,独立的敲打一遍,相信我,你的感悟会异常深刻的。
指针和数组笔试题解析
数组名表示数组首元素的地址
2个例外
1.sizeof(数组名),数组名表示整个数组,计算的是数组的总大小,单位是字节
2.&数组名,数组名表示整个数组 &数组名 取出的是整个数组的地址
除此之外,所遇到的数组名都表示数组首元素的地址
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//4×4=16个字节
printf("%d\n",sizeof(a+0));//a往后移(int*)0,即得到(int)1的地址,一个地址的大小字节数是4/8,4/8取决于你是用x32/x64。
printf("%d\n",sizeof(*a));//取出a首元素地址里的内容,即为(int)1,4个字节
printf("%d\n",sizeof(a+1));//a往后移(int*)1,即得到(int)2的地址,一个地址的大小字节数是4/8
printf("%d\n",sizeof(a[1]));//值(int)1,4字节
printf("%d\n",sizeof(&a));//4/8,取出整个数组的地址
printf("%d\n",sizeof(*&a));//*与&抵消,相当于sizeof(a)
printf("%d\n",sizeof(&a+1));//4/8,跳过整个数组的地址,
printf("%d\n",sizeof(&a[0]));//取出第一个元素的地址,4/8
printf("%d\n",sizeof(&a[0]+1));//取出第二个元素的地址,4/8
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//6,不包括'\0'
printf("%d\n", sizeof(arr+0));//arr往后移(int*)0,即得到(char)a的地址,一个地址的大小字节数是4/8,4/8取决于你是用x32/x64。
printf("%d\n", sizeof(*arr));//取出arr首元素地址里的内容,即为(char)a,1个字节
printf("%d\n", sizeof(arr[1]));//(char)b,1个字节
printf("%d\n", sizeof(&arr));//4/8,取出整个数组的地址
printf("%d\n", sizeof(&arr+1));//4/8,跳过整个数组的地址,
printf("%d\n", sizeof(&arr[0]+1));//(char)b的地址,4个字节
printf("%d\n", strlen(arr));//随机值,因为不知道什么时候会出现'\0'
printf("%d\n", strlen(arr+0));//同上
printf("%d\n", strlen(*arr));//取到(char)a,并把它当做ASCII码值所指向的地址向后访问
//相当于随机找了个地址去访问,野指针,是存在问题的,错误代码
printf("%d\n", strlen(arr[1]));//同上,err
printf("%d\n", strlen(&arr));//随机值,同第二个
printf("%d\n", strlen(&arr+1));//跳过整个数组向后访问,随机值
printf("%d\n", strlen(&arr[0]+1));//随机值,同第二个
接下来的代码,我就不细说了,答案会给出,分析由各位自行分析
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr+0));//4
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4
printf("%d\n", sizeof(&arr+1));//4
printf("%d\n", sizeof(&arr[0]+1));//4
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr+0));//6
printf("%d\n", strlen(*arr));//err
printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//5
int a[3][4] = {0};//基于32位情况下
printf("%d\n",sizeof(a));//12×4=48整个数组的字节大小
printf("%d\n",sizeof(a[0][0]));//4 整形0的字节大小
printf("%d\n",sizeof(a[0]));//4×4=16 第一行数组的字节大小
printf("%d\n",sizeof(a[0]+1));//4 第一行第二位数的地址
printf("%d\n",sizeof(*(a[0]+1)));//4 第一行第二位数的值
printf("%d\n",sizeof(a+1));//4 第二行的地址
printf("%d\n",sizeof(*(a+1)));//4×4=16 第二行数组的字节大小
printf("%d\n",sizeof(&a[0]+1)); //4 第二行的地址
printf("%d\n",sizeof(*(&a[0]+1)));//16 第二行数组的字节大小
printf("%d\n",sizeof(*a));//16 第一行数组的字节大小
printf("%d\n",sizeof(a[3]));//16 虽然没有定义第四行,但是sizeof仍会计算第四行的大小,但不会访问,所以问题不大
指针在这里就基本结束啦,欢迎小伙伴来阅读指正哦~