C语言指针进阶02

本文介绍了函数指针数组的使用,如作为转移表实现计算器功能,以及回调函数的概念及其在优化代码中的应用,包括冒泡排序的回调版本和qsort库函数的使用示例。
摘要由CSDN通过智能技术生成

指针进阶2

1. 函数指针数组

数组:

是一个存放相同数据类型的存储空间

指针进阶1中我们已经了解了:

指针数组:

char* arr[5];//字符指针数组-数组-存放的是字符指针
int* arr[10];//整形指针数组-数组-存放的是整形指针
//数组的每个元素是char* 或者int* 

函数指针数组与此类似:

函数指针数组,顾名思义-数组-存放的是函数指针,存放的是函数的地址

函数指针:

  • 函数指针数组的定义:
int (*parr1[10])();//与函数指针非常相似
//函数指针的定义: int(*parr1)()

parr1先与[]组合,说明了parr1是数组

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

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

例子:(计算器)

//加减乘除函数的定义
 //加法
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");
}

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("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;
}

利用函数指针数组实现:

int main()
{
	//模式的选择
	int input = 0;
	//输入的两个数
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d %d", &input);
		//函数指针数组 - 转移表
		int(*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
			         //因为只是只是声明了函数指针的类型,并不使用参数int后面不加参数
		if (0 == input)
		{
			printf("退出计算机\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数: ");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
            //(*pfarr)[input](x, y)
			printf("ret = %d\n", ret);
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input != 0);
		return 0;
}

函数指针可以通过两种方式进行调用:

使用函数指针名称后面跟随参数列表,或者使用解引用操作符(*)来调用指针所指向的函数并传递参数列表。

因此,ret = pfArr[input](x, y);ret = (*pfArr[input])(x, y);都会调用指针所指向的函数,并传递参数x和y,然后将返回值赋给ret变量。

函数:Add(int x, int y) 函数名就是该函数的地址,即Add<->&Add

两种方式的效果是相同的。

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

int a = 10;
int b = 20;
int c = 30;
int* arr[] = {&a, &b, &c};//整形指针数组
int* (* p)[3] = &arr;//p是指针,是指向整形指针数组的指针
//函数指针数组-数组-存放的是函数的地址
int (*pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};//pfArr是函数指针数组
p = &pfArr;
int (*(*p)[5])(int, int) = &pfArr;

3. 回调函数

依赖函数指针,有了函数指针才能实现回调函数

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

利用回调函数优化计算器代码

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);   //把Add的地址传到calc函数参数中
			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(qsort是一个库函数)

  • 函数可以直接使用
  • 函数可以用来排序任意类型的数据
  • 底层使用的快速排序的方法,对数据进行排序

对数据排序的一些方法:

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 快速排序
  1. 冒泡排序
    1. 冒泡排序思想:两两相邻的元素进行比较
    2. 冒泡排序的具体实现思路:

Snipaste_2023-09-07_20-57-50

			3. **冒泡排序的弊端**:只能排序一类数据

			4. **冒泡算法的代码实现**:

```c
void print_arr(int* pc, int sz)//int arr[],本质上是int* arr,如果写成int arr[],只是便于初学者理解
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", pc[i]);
	}
	printf("\n");
}
void bubble_sort(int* pc, int sz)
{
	//趟数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (pc[j] > pc[j + 1])
			{
				int tmp = pc[j];
				pc[j] = pc[j + 1];
				pc[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;
}


```

**注意**:

在函数定义中,**形参是用来接收函数调用时传递进来的实参的变量。形参和实参可以同名,但是它们是两个不同的变量,分别在不同的作用域中**。

当形参和实参同名时,函数内部的代码会优先使用形参的值,而不是外部传入的实参的值。这意味着在函数内部,同名的形参会屏蔽外部的同名实参。

示例代码如下:

```c
def add(x, y):
    result = x + y
    return result

x = 2
y = 3
print(add(x, y))  # 输出 5
```

在这个例子中,函数`add`的形参`x`和`y`与外部的同名变量`x`和`y`重名,但它们是两个不同的变量。在函数内部,`x`和`y`分别代表函数调用时传入的实参值,而外部的`x`和`y`保持不变。函数内部的计算结果会使用形参的值,所以最终输出结果为5。

需要注意的是,虽然形参和实参可以同名,但这样的命名方式会增加代码的可读性和维护性的难度,容易引起混淆和错误,不建议在实际开发中使用。最好避免同名的形参和实参,或者使用更具描述性的命名方式来避免混淆。



**关于void***

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

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

void* 无具体类型的指针

int main()
{
	char c = 'w';

	char* pc = &c;

	int a = 100;
	//int* p = &c;
	//       char*
	void* pv = &c;//char*
	pv = &a;//int*

	return 0;
}
  1. 使用回调函数,模拟实现qsort(采用冒泡的方式)
#include<stdio.h>
#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);
}

//测试qsort排序结构体数据
struct Stu
{
	char name[20];
	int age;
};
//结构体数据怎么比较呢?
//1. 按照年龄比较
//2. 按照名字比较

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

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_age);
}

//2. 按照名字比较

int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void test3()
{
	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();
	//test2();
	test3();
	return 0;
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

繁星ベ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值