指针进阶(二)

指针进阶

5.函数指针

补:
&函数名就是函数的地址
函数名也是函数的地址

c 复制代码
代码演示:
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//&函数名就是函数的地址
	//函数名也是函数的地址
	printf("%p\n", &Add);
	printf("%p\n", Add);
}

运行结果:

c 复制代码
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
    //函数指针变量:
	int (*pf1)(int, int) = Add;//pf1就是函数指针变量
    //形式1:
	int (* pf2)(int, int) = &Add;
	int ret = (* pf2)(2, 3);
    //形式2:
	int (* pf2)(int, int) = Add;
	int ret = pf2(2, 3);
    //形式3
    int (* pf2)(int, int) = Add;
	int ret = Add(2, 3);
	printf("%d\n", ret);
	return 0;
}

来看以下两个代码:

c 复制代码
//代码1
(* (void (*)( )) 0 )( );
//代码2
void (* signal (int , void(*)(int)) )(int);

分析:

//代码一:

将0强制类型转化为(void (*)( )),解引用函数指针类型,出入参数为空

//代码二:

是一次函数声明,声明的是signal函数

第一个是int类型

第二个是函数指针类型,该类型是void( * )(int)。该函数指针指向的函数参数是int,返回类型是void

signal函数的返回类型也是函数指针类型,该类型是void( * )(int),该函数指针指向的函数,参数是int,返回类型是void

代码2太复杂,如何简化:

c 复制代码
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组

c 复制代码
//比如:
int *arr[10];
//数组的每个元素是int*

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

c 复制代码
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

答案是 :parr1
分析:

parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是什么呢?

是 int (*)( ) 类型的函数指针。

看如下代码进一步理解函数指针数组:

c 复制代码
int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}
int main()
{
	int (*pf1)(int, int) = &Add;
	int (*pf2)(int, int) = &Sub;
	//数组中存放类型相同的多个元素
	int (*pfArr[4])(int, int) = { &Add, &Sub };//pfArr 是函数指针数组 - 存放函数指针的数组
	return 0;
}

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

例子:计算器

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

用函数指针实现

c 复制代码
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;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//函数指针数组 - 转移表
		int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
		//                          0     1     2   3    4
		if (0 == input)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("ret = %d\n", ret);
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input);

	return 0;
}

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

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

指针指向一个 数组 ,数组的元素都是 函数指针 ;
如何定义?

8. 回调函数

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

c 复制代码
//代码演示:
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;
}

void calc(int (*pf)(int,int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入2个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}

int main()
{
	int input = 0;

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误, 重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

看图片理解下回调函数:

案例:使用回调函数,模拟实现qsort(采用冒泡的方式)。

一般冒泡排序:

c 复制代码
//代码演示:
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void bubble_sort(int arr[], int sz)
{
	//趟数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	//数据
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_sort(arr, sz);//冒泡排序
	print_arr(arr, sz);
	return 0;
}

运行结果:

这种冒泡排序缺陷:

qsort(采用冒泡的方式):

了解以下qsort()函数:

c 复制代码
//代码案例:
#include <stdlib.h>
#include <string.h>
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
//测试qsort排序整型数据
void test1()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}
int main()
{
	test1();
	return 0;
}

运行结果:

补:void*

1.void* 类型的指针 - 不能进行解引用操作符,也不能进行±整数的操作

2.void* 类型的指针是用来存放任意类型数据的地址

3.void* 无具体类型的指针

c 复制代码
代码演示:
int main()
{
	char c = 'w';
	char* pc = &c;

	int a = 100;
	//int* p = &c;//不可以存放char*类型

	void* pv = &c;//存放char*
	pv = &a;//存放int*

	return 0;
}

案例:测试qsort排序结构体数据

结构体数据怎么比较呢?

  1. 按照年龄比较
c 复制代码
#include<stdio.h>
#include<stdio.h>
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;
}
void test1()
{
	struct Stu arr[] = { {"zhanhsan", 20}, {"lisi", 30}, {"wangwu", 12} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{
	test1();
	return 0;
}
  1. 按照名字比较
c 复制代码
 struct Stu
{
	char name[20];
	int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{
	struct Stu arr[] = { {"zhanhsan", 20}, {"lisi", 30}, {"wangwu", 12} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
	test1();
	return 0;
}

💘不知不觉,指针进阶(二)以告一段落。通读全文的你肯定收获满满,不久的将来会继续更新指针进阶的内容,让我们继续为C语言学习共同奋进!!!

相关推荐
DdddJMs__135几秒前
C语言 | Leetcode C语言题解之第461题汉明距离
c语言·leetcode·题解
gopher95111 分钟前
final,finally,finalize的区别
java·开发语言·jvm
吃什么芹菜卷4 分钟前
深度学习:词嵌入embedding和Word2Vec
人工智能·算法·机器学习
m0_687399847 分钟前
QT combox 前缀匹配
开发语言·数据库·qt
wclass-zhengge8 分钟前
数据结构与算法篇(树 - 常见术语)
数据结构·算法
labuladuo52013 分钟前
AtCoder Beginner Contest 372 F题(dp)
c++·算法·动态规划
夜雨翦春韭15 分钟前
【代码随想录Day31】贪心算法Part05
java·数据结构·算法·leetcode·贪心算法
汤兰月15 分钟前
Python中的观察者模式:从基础到实战
开发语言·python·观察者模式
DieSnowK16 分钟前
[C++][第三方库][httplib]详细讲解
服务器·开发语言·c++·http·第三方库·新手向·httplib