C语言进阶笔记(三) | 指针详解(下)

目录

函数指针

看一段代码

 再举个栗子

函数指针数组

举个栗子

函数指针数组的用途:转移表

函数指针数组指针

回调函数

回调函数的使用

qsort函数

使用回调函数,模拟实现qsort


函数指针

数组指针是指向数组的指针;
那么函数指针不难理解,是指向函数的指针,存放函数地址的指针。

看一段代码

#include <stdio.h>

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

int main()
{
    printf("%p\n", test);
    printf("%p\n", &test);
    return 0;
}

输出的结果

输出的是两个地址,这两个地址是 test 函数的地址。 那我们的函数的地址怎么保存呢?

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

void (*pfun1)();
void *pfun2();

pfun1和pfun2哪个有能力存放test函数的地址? 

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

 再举个栗子

#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("%d\n", Add(a, b));
	
	//&函数名 和 函数名 都是函数的地址
	printf("%p\n", Add);
	printf("%p\n", &Add);

	int(*pa)(int, int) = Add;//函数指针的存储   int(*)(int,int) - 变量类型
	printf("%d\n",(*pa)(2, 3));//5 - 推荐写法 - 好理解
	printf("%d\n",(pa)(2, 3));//5
	printf("%d\n",(**pa)(2, 3));//5
	printf("%d\n",(***pa)(2, 3));//5
	//这里的*其实只是摆设,没有作用

	return 0;
}

函数指针数组

数组是一个存放相同类型数据的存储空间,前面已经学习了指针数组,比如:

int *arr[10];

把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

答案是:parr1

parr1 先和 [ ] 结合,说明parr1是数组,数组的内容是 int (*)() 类型的函数指针。

举个栗子

#include<stdio.h>

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(*parr[4])(int, int) = { Add,Sub,Mul,Div };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d\n", parr[i](2, 3));
	}
	return 0;
}

函数指针数组的用途:转移表

例子:实现计算器

#include<stdio.h>

void menu()
{
	printf("*************************\n");
	printf(" 1:add           2:sub \n");
	printf(" 3:mul           4:div \n");
	printf("         0:exit        \n");
	printf("*************************\n");
}

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 input = 0;
//	int x = 0;
//	int y = 0;
//	do 
//	{
//		menu();
//		printf("请选择:");
//		scanf("%d", &input);
//		
//		switch (input)
//		{
//		case 1:
//			printf("请输入两个整数:");
//			scanf("%d%d", &x, &y);
//			printf("%d\n", Add(x, y));
//			break;
//		case 2:
//			printf("请输入两个整数:");
//			scanf("%d%d", &x, &y);
//			printf("%d\n", Sub(x, y));
//			break;
//		case 3:
//			printf("请输入两个整数:");
//			scanf("%d%d", &x, &y);
//			printf("%d\n", Mul(x, y));
//			break;
//		case 4:
//			printf("请输入两个整数:");
//			scanf("%d%d", &x, &y);
//			printf("%d\n", Div(x, y));
//			break;
//		case 0:
//			printf("退出\n");
//			break;
//		default:
//			printf("选择错误\n");
//			break;
//		}
//	} while (input);
//
//	return 0;
//}

 
//利用函数指针数组写法
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int(*pfArr[])(int, int) = { 0,Add,Sub,Mul,Div };//pfArr是一个函数指针数组 - 转移表
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if(input>=1&&input<=4)
		{
			printf("请输入两个操作数:");
			scanf("%d%d", &x, &y);
			int ret = pfArr[input](x,y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出\n");
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);

	return 0;
}

函数指针数组指针

函数指针数组指针,即指向函数指针数组的指针,是一个指针,指针指向一个 数组 ,数组的元素都是函数指针。

那么它如何定义呢?看代码 

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr; //取出数组的地址

	int(*pf)(int, int);//pf是一个函数指针
	int(*pfArr[4])(int, int);//pfArr是一个函数指针数组
	int(*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个函数指针数组指针
	return 0;
}

回调函数

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

举例演示 

#include<stdio.h>

void print(char arr[])
{
	printf("%s\n",arr);
}

void test(void (*p)(char))
{
	p("haha");
}

int main()
{
	test(print);
	return 0;
}

输出结果

回调函数的使用

首先演示一下qsort函数的使用

 qsort函数

//qsort - quick sort - 可以排序任意类型的数据
//void qsort(void* base,size_t num, size_t width, int(__cdecl* compare)(const void* elem1, const void* elem2));
//4个参数:
//1.目标数组的首元素地址
//2.数组大小(以元素为单位)
//3.元素大小(字节)
//4.比较函数
// 
//void* 类型可以接受任意类型的指针
//因为无类型无法确定访问的空间大小 - 不能进行解引用(*)操作 - 不能进行+-整数的操作
// 
//排序整型数组
#include<stdio.h>
#include<stdlib.h>//qsort

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;//规定e1<e2,返回小于0的值  =,返回0  >,返回大于0的值
}

int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz , sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

使用回调函数,模拟实现qsort

采用冒泡的方式 

#include<stdio.h>
#include<string.h>


int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}


int cmp_float(const void* e1, const void* e2)
{
	if (*(float*)e1 < *(float*)e2)
		return -1;
	else if (*(float*)e1 > *(float*)e2)
		return 1;
	else
		return 0;
}


struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
	//return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;
}

int cmp_stu_by_name(const void* e1, const void* e2)
{
	//比较名字是比较字符串
	//字符串不能直接用>、<比较,应用strcmp函数
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}


void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *(buf1 + i);
		*(buf1 + i) = *(buf2 + i);
		*(buf2 + i) = tmp;
	}
}


//实现bubble_sort函数的程序员不知道未来排序的数据类型 - void*
//那便也不知道待比较的两个元素类型 - void*
void bubble_sort(void* base, int sz, int width,int(*cmp)(void* e1,void* e2))
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		//每趟比较的对数
		for (j = 0; j < sz - 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 test1()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用bubble_sort函数的程序员知道自己要排序的数据
	//也就知道如何比较排序数组中的元素 - cmp_int
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}


void test2()
{
	float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0 };
	int sz = sizeof(f) / sizeof(f[0]);
	bubble_sort(f, sz, sizeof(f[0]), cmp_float);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%.1f ", f[i]);//%.nf - 保留n为小数
	}
	printf("\n");
}


void test3()
{
	struct Stu s[3] = { {"张三",20},{"李四",30},{"王五",10} };
	int sz = sizeof(s) / sizeof(s[0]);
	//以年龄排序
	//qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	//以名字比较
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s %d\n", s[i].name, s[i].age);
	}
	return 0;
}


int main()
{
	test1();//int
	test2();//float
	test3();//结构体数组

	return 0;
}

学完指针,在练习题专栏有对应的指针笔试题哦,快去练习巩固一下吧~ 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天青i

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

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

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

打赏作者

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

抵扣说明:

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

余额充值