指针终章

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

举个简单的例子供大家理解一下

上代码

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仍会计算第四行的大小,但不会访问,所以问题不大

指针在这里就基本结束啦,欢迎小伙伴来阅读指正哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值