C语言----指针(G3)1. 字符指针2. 指针数组3.数组指针4. 数组传参和指针传参5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数

回顾一下上节课


函数指针:


void test()
{
	printf("haha\n");
}

int main()
{

	printf("%p\n", test);
	printf("%p\n", &test);

	void(*pf)() = &test;//这个就是函数指针
	//void(*pf)() = test;//这样写也可以。函数名就是地址。
	test();
	(*pf)();//通过pf调用test函数。
	pf();//也可以这样写 这个*其实没有意义的。
    //函数名就是地址 用地址写也可以。


	return 0;
}

函数指针数组;

//实现一个计算器
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 menu()
{
	

	printf("*********************\n");
	printf("****1.Add  2.Sub ****\n");
	printf("****3.Mul  4.Div ****\n");
	printf("****0.exit       ****\n");
	printf("*********************\n");
	printf("*********************\n");
	printf("请输入序号\n");

}

int main()
{
	//在函数指针变量名后面加一个[]就是函数指针数组。
	int(*pf[5])(int, int) = {0,Add,Sub,Mul,Div };//这个就是一个函数指针数组。
//返回值        参数类型     0就是空指针,也可以写NULL
	int n;
	do
	{
		
		menu();
		//判断输入的合法性;
		while (scanf("%d", &n) != EOF)
		{
			if ((n > 4) && (n<0))
			{
				printf("输入错误,请重新输入!!\n");

			}
			if ((n <= 4) && (n >= 0))
			{
				break;
			}
		}
		if (n == 0)
		{
			printf("退出计算器!\n");
			break;
		}

		int x, y;
		printf("请输入运算的两个值:");
		scanf("%d %d", &x, &y);

		int red = pf[n](x, y);
		printf("运算结果为:%d\n", red);




	} while (n);

	return 0;

}


7. 指向函数指针数组的指针

就是一个指针 指向函数指针数组的指针

定义:


int main()
{
	int arr1[10] = { 1,2,3,4,5,6 };
	int(*p1)[10] = &arr1; //p是一个数组指针。

	int* arr2[5]; //这个是指针数组
	int* (*p2)[5] = &arr2;

	//类比

	int (*pf)(int, int) = &Add;
	//这个是一个函数指针。

	int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
    //函数名和&函数名都是函数的地址 都一样。
	//这个是函数指针数组。

	//现在我们要写一个 函数指针数组的指针。//每个元素是函数指针。
    // 类型为int (*)(int, int)
	//指针数组就是 类型  加 (*数组名)[n]

	int(*(*p3)[4])(int, int) = &pfarr;

    //p3是一个指针,指向函数指针数组的指针。



	return 0;
}

使用:


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(*p)(int, int) = Add;
	//p是函数指针变量
	int (*pfarr[4])(int, int) = { Add, Sub, Mul,Div };
	//pfarr是函数指针数组名

	int (*(*p3)[4])(int, int) = &pfarr;
	//p3指向函数指针数组的指针。

	printf("%d",(*p3)[0](3, 4));//7


	return 0;
}




8. 回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。

简单说就是:把A函数的指针,作为参数传递给B函数,在B函数内,通过A函数的指针来调用A函数时,我们就说这是一个回调函数。

简单的例子:


void test()
{
	printf("haha");

}

void print_haha(void(*p)())
{
	if (1)
	{
		p();
	}
}

int main()
{
	void (*pf)() = test;
	print_haha(pf);


	//结果为haha

	return 0;
}


用法:(这个可以对第一次写的那个计算器冗余的改进方法。)


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 menu()
{
	printf("请输入选择:\n");
	printf("**********************************\n");
	printf("**********1.Add      2.Sub  ******\n");
	printf("**********3.Mul      4.Div  ******\n");
	printf("**********0.exit            ******\n");
	printf("**********************************\n");
	printf("**********************************\n");
}


//这个代码就是大大缩减了代码的冗余
void mmm(int (*p)(int, int))//这里用函数指针类型进行接收。
{
	printf("请输入两个数字:\n");
	int x, y;
	scanf("%d%d", &x, &y);
	printf("%d\n", p(x, y));

}
//这里用到了回调函数
//在B函数通过地址来调用A函数






int main()
{
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出计算器!!\n");
			break;
		case 1:
			mmm(Add);
			break;
		case 2:
			mmm(Sub);
			break;
		case 3:
			mmm(Mul);
			break;
		case 4:
			mmm(Div);
			break;
		default:
			printf("输入错误!!!\n");
		}

		


	} while (input);
	


	return 0;
}

案例:

qsort函数:

qsort是一个库函数 ,基于快速排序算法实现的一个排序的函数。这个qsort函数对任意类型的数据都可以进行排序。功能很强大。我们写的排序并不是任意数据都可以排序 ,只能排一类数据。

//
//	//qsort库函数有四个参数。
//	void qsort(void* base, //待排序数据的起始位置,也就数组的首元素地址
//
//		size_t num,//数组的元素个数
//
//		size_t width, //一个元素的字节大小。
//
//		int(* com)(const void* elem1, const void* elem2));
com是比较函数,  elem1和elem2是待比较的两个元素的地址。


#include<stdlib.h>
#include<stdio.h>



//这里e1和e2是void*的指针,是一个无确切类型的指针,不能直接进行解引用的
//void* 是用来接收任意类型的指针的。不能直接解引用和加减。
//可以先用()强制类型转换成某一类型的指针就可以使用了。
//而且被const修饰
int cmp(const void* e1, const void* e2)
{
	第一种方法。
	//if (*(int*)e1 > *(int*)e2)
	//{
	//	return 1;
	//}
	//else if (*(int*)e1 == *(int*)e2)
	//{
	//	return 0;
	//}
	//else
	//{
	//	return -1;
	//}
	// 
	//第二种方法
	return (*(int*)e1 - *(int*)e2);
	//如果e1大于e2返回一个正数。
	//如果e1大于e2返回一个正数。
	//如果e1等于e2返回一个0。


}
 
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, 10, sizeof(arr[0]), cmp);



	//打印结果
	for (int i = 0; i < sz; i++)
	{
		printf("%d", arr[i]);
	}
	//结果为0123456789



	return 0;
 }

运用qsort排序字符串

//使用qsort排序结构体
struct stu
{
	char name;//名字
	int age;//年龄
	float score;//成绩
};

//按照年龄来排的
int cmp_stu_age(const void* e1, const void* e2)//这里的e1和e2是指向结构体的指针。
{
	return (((struct stu*)e1)->age - ((struct stu*)e2)->age);
}


//按照名字来排的
int cmp_stu_name(const void* e1, const void* e2)//这里的e1和e2是指向结构体的指针。
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}


int main()
{
	struct stu arr[3] = {{"liu",21,99.8} , {"guan",19,100.0}, {"zhang", 20,99.9} };

	qsort(arr, 3, sizeof(arr[0]), cmp_stu_age);
	//这里就实现了按年龄从小到大排序
//可以通过监视窗口自己观看。


	return 0;
}

模拟实现qsort函数

qsort函数有四个参数

1,数组的首地址

2,数组的大小

3,数组每个元素的字节大小

4,判断函数的地址

//模拟qsort函数
struct stu
{
	char name[10];
	int age;
	float scort;
};
//交换任意两个数据的函数实现
//这里不知道是什么类型的数据,但是我们传参的时候把元素所占的字节大小也传了过来
//这样就保证了,我们可以一个字节一个字节的进行交换。
void reverse(char* left, char* right,int width)
{
	//单个字符交换。
	for (int i = 0; i < width; i++)
	{
		char mid = *left;
		*left = *right;
		*right = mid;
		right++;
		left++;
	}



}

//注意这里的void*是为了保证能接收任何类型的函数
my_qsort(void* base, int sz, int width, int (*cmp)(void*, void*))
//注意int(*)(void*)(void*)函数指针类型  
{																 
//接收那个自定义的判断函数。

	for (int i = 0; i < sz-1; i++)//这个是排序的趟数
	{
		//每一趟具体情况
		for (int j = 0; j < sz - 1 - i; j++)
		{
			//cmp函数是自己用qsort函数是写的判断函数要在这里使用  
            //默认返回大于0的数交换,默认升序
			//并不知道base指向的类型,所以不能直接加j,要乘以宽度。
			//它的两个参数,是两个元素的首地字节地址,但是void*不能直接加减。
            //要先进行强制类型转换。
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
            //这个函数是qsort函数的第四个参数指向的函数
			{
				//任意类型的交换函数,这里要用到每个元素的字节大小。
				reverse((char*)base + j * width, (char*)base + (j + 1) * width,width);

			}

		}
	}

}


int cmp(const void* e1, const void* e2)
{

	return *(int*)e1 - *(int*)e2;
}


int main()
{
	int arr[4] = { 4,6,2,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	//第一个数组开头的地址,
	//第二个数组的元素个数
	//每个元素的字节大小
	//在自定义的判断函数,返回大于0他就交换。
	my_qsort(arr, sz, sizeof(arr[0]), cmp);
	//默认升序。

	for (int i = 0; i < sz; i++)
	{
		printf("%d", arr[i]);
	}





	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小峰同学&&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值