【C语言】指针进阶知识终章

这篇具有很好参考价值的文章主要介绍了【C语言】指针进阶知识终章。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

✨作者:@平凡的人1

✨专栏:《C语言从0到1》

✨一句话:凡是过往,皆为序章

✨说明: 过去无可挽回, 未来可以改变


🌹感谢您的点赞与关注,同时欢迎各位有空来访我的🍁平凡舍


🚀前言

回顾我们前面学习了指针数组、数组指针以及简单介绍了函数指针,传参问题等。下面我们将在这些学习过的内容之上继续延展下去,通过这一篇博客,你可以收获更多的知识与内容,同时夯实自己的基础。本篇内容可能比较多,请耐心仔细阅读!💖


🚀有趣的代码

开始之前,基于前面的基础,我们先来看看两个有趣的代码

//代码1 
(*(void (*)())0)();

//代码2
void (*signal(int , void(*)(int)))(int);

这两个代码是什么意思呢?先想一想

代码1:

想看里面的部分void(*p)();p是函数指针,所以对于void(*)()是函数指针类型,0本身是个值,0之前放了个类型,强制类型转换,然后进行解引用。所以说代码1是一次函数调用,调用的0作为地址处的函数.

1.把0强制类型转换为:无参,返回类型是void的函数的地址

2.调用0地址处的这个函数

代码2:

signal是函数名,有两个参数,一个是整型,一个是函数指针类型,此时简单来说只剩下void(*)(int),这又是一个函数指针类型。所以说,

代码2是一次函数声明,声明的signal函数的第一个参数的类型是int,第二参数的类型是函数指针,该函数指针指向的函数参数是int,返回类型是void,signal函数的返回类型也是一个函数指针

其实这看起来是难以理解的,所以我们可以用typedf(起别名)来简化一下,更加容易认识代码:

所以,我们要学会去拆分一下代码,不会导致看不懂别人写的代码是什么意思。

我们前面学到了函数指针,但是却没有举例说到函数指针的用途,函数指针究竟能够去做些什么呢?下面,我们一起来看一看。👇


🚀简单计算器

我们将基于简单计算器这个例子来阐述函数指针的用处在于哪,或者说怎么去用上函数指针呢?我们先来简单模拟实现一下简单计算器(基于整型类型)

实现整型加减乘除的功能

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

简单测试运行一下:

你会发现,输入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;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请选择2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请选择2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请选择2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请选择2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	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;
}

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

简单测试运行一下:

没有任何问题,解决了代码的冗余问题。这就是函数指针的作用。

通过函数地址传递给函数参数,进入函数内部,去调用函数,这就是回调函数。后面会讲到。

【C语言】指针进阶知识终章


🚀函数指针数组

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

开始之前,我们先来理解函数指针数组:把函数和指针放在数组中,其实就是函数指针数组,怎么理解呢?看一段代码:

怎么去调用里面的函数呢?

所以函数指针数组有什么用?怎么去用?下面来进行演示

还是刚开始冗余代码版本的计算器:

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

我们想一下,如果要添加实现x&y,x^y,x>>y,x<<y的功能,此时就是添加4,5,6,7,8之类的选项,case的选项越来越多以此类推,代码会变得越来越长,这时候,把代码写得整洁一些:把switch语句去掉,创建一个函数指针数组存放函数,通过输入的选择作为下标去调用即可,下面我们来看看代码的修改:

#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;
	int ret = 0;
	int (*pfArr[5])(int, int) = { 0,Add, Sub,Mul,Div};
	//添加0进去,使之下标对应起来
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//直接让input作为下标
		if (input == 0)
		{
			printf("退出计算器");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请选择2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

这样修改的好处在于,以后想添加新的功能,只需要把函数的地址放在数组里面即可,改变范围即可,稍微调整一下代码即可。通过函数指针数组便于以后修改代码。通过这个简单的例子,演示了函数指针数组的作用。下面我们来简单测试一下:

🚩指向函数指针数组的指针

在这里,顺便提一提指向函数指针数组的指针。前面我们写了函数指针数组,是数组,我们对它&,放到一个指针里面即可。

这里基于上述的函数指针数组来用代码简单表示一下:

当然,你会发现,可以套娃套下去…这里就不展开说明了

【C语言】指针进阶知识终章


🚀回调函数

刚开始,实现计算机的时候有说到:

这就是用了回调函数的机制,什么是回调函数?

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

说到这里,太抽象了,难以理解,这时候,我们请出一个例子qsort函数的使用。

说到qsort函数,我们先来说一说冒泡排序

🚩冒泡排序优化版

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;
		//用来判断数组是否有序,提高效率
		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;
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}
#include <stdio.h>
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1,0 };
	//利用冒泡排序把数组排成升序
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

虽然优化了一下下,提高了效率,但是不管怎么优化,都只能排序整型数组。那怎么办呢?qsort

🚩qsort函数的使用

【C语言】指针进阶知识终章

使用快速排序的思想实现的一个排序函数

下面,我们来简单理解一下qsort函数的参数的意思:

可以看到,比较函数有void*,所以我们很有必要来理解一下void*指针:

这时候,如果用void*来接收,就不会报警告了:

void*是无具体类型的指针,这种指针可以接收任意类型的地址,void*是无具体类型的指针,所以不能解引用操作,也不能±整数操作。这里的参数是void*的原因是因为不知道传过来的类型的指针是什么,所以定义为void*

下面我们还是通过上面的例子来对qsort函数进行简单应用:(记得引用头文件 #include <stdlib.h>)

通过qsort成功实现排序,那能不能实现降序?

我们只要通过改变e1和e2相减的位置即可实现。使之逻辑相反。这个就是回调函数来实现qsort的功能!

这里只是qsort的基本使用。

上面是利用qsort函数来排序整型的,下面我们利用qsort函数来排序结构体

通过结构体的名字进行排序:

通过结构体的年龄来进行排序:

好了,通过上面,已经对qsort有了一定的认识,并且会逐渐的运用,这时候,想想:怎么把冒泡排序改造一下

🚩冒泡排序通用版

把冒泡排序改造成类似qsort函数的实现

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

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;
}
void bubble_sort(void *base,int sz,int width,int(*cmp)(const void*e1,const void *e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;
		//用来判断数组是否有序,提高效率
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//通过利用强转base为(char*)乘以宽度的多少来进行比较
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
			{
				Swap((char*)base + j * width, ((char*)base + (j + 1) * width),width);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

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

int main()
{
	test();
}

运行成功!在这里,我们稍微改造了冒泡排序,现在,来通过冒泡排序,排序结构体:

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 Swap(char* a, char* b,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *a;
		*a = *b;
		*b = tmp;
		a++;
		b++;
	}
}

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;
}
void bubble_sort(void *base,int sz,int width,int(*cmp)(const void*e1,const void *e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;
		//用来判断数组是否有序,提高效率
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//通过利用强转base为(char*)乘以宽度的多少来进行比较
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
			{
				Swap((char*)base + j * width, ((char*)base + (j + 1) * width),width);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

void test()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void test1()
{
	struct Stu s[] = { {"zhangsan",1} ,{"lisi",2},{"wangwu",3} };
	int sz = sizeof(s) / sizeof(s[0]);
	//qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%d\n", s[i].age);
	}
}

int main()
{
	//test();
	test1();
}

简单测试运行:

好了,关于其他排序的话这里就先不展开了,就先到这里结束了!


🚀结语

好了,通过本篇博客我们刚开始看了两个有趣的代码,以及模拟实现简单计算器,认识到了什么是函数指针数组,以及使用的例子,还略微提及了指向函数指针数组的指针,以及最后的回调函数,以及后续通过回调函数而展开冒泡排序与qsort函数的实现!如果觉得还不错的话,多多支持哦!这次博客也到此结束了!>🌹文章来源地址https://www.toymoban.com/news/detail-435320.html

到了这里,关于【C语言】指针进阶知识终章的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(35)
  • 【C语言进阶】指针数组 —— 数组指针

    🎬 鸽芷咕 : 个人主页  🔥 个人专栏 : 《C语言进阶篇》 《C语言初阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,前面我们已经把指针大部分内容给学完了,今天就给大家带来数组指针or指针数组!    ⛳️ 很多说这俩名字不是差不

    2024年02月14日
    浏览(28)
  • 进阶C语言-指针的进阶(三)

    📝关于 qsort函数 ,我们可以先去cpluplus网站上面了解一下: ✅运行示例: 🔎我们发现上述代码只适用于整型数据,那么我们是否能写出更一般的更普遍的代码呢? ✅运行示例: 📖运行流程如下: 🔎按照年龄排序: ✅运行示例: 🔎按照姓名排序: ✅运行示例: 好啦,

    2024年02月05日
    浏览(34)
  • 进阶C语言-指针的进阶(二)

    数组指针 - 指向数组的指针 - 存放的是数组的地址 - 数组名就是数组的地址。 函数指针 - 指向函数的指针 - 存放的是函数的地址 - 函数名就是函数的地址。 🔭阅读下面这两段代码: 🔭 指针数组: 字符指针数组 - 数组 - 存放的是字符指针 整型指针数组 - 数组 - 存放的是整

    2024年02月07日
    浏览(30)
  • 进阶C语言-指针的进阶(上)

    通过前面的学习,我们了解了指针的概念: 指针变量就是一个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定为 4/8 个字节( 32位平台/64位平台 )。 指针是有类型的,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。 指针的运算

    2024年02月09日
    浏览(26)
  • 进阶C语言-指针的进阶(一)

    通过前面的学习,我们了解了指针的概念: 指针变量就是一个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定为 4/8 个字节( 32位平台/64位平台 )。 指针是有类型的,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。 指针的运算

    2024年02月07日
    浏览(28)
  • 进阶C语言-指针的进阶(中)

    数组指针 - 指向数组的指针 - 存放的是数组的地址 - 数组名就是数组的地址。 函数指针 - 指向函数的指针 - 存放的是函数的地址 - 函数名就是函数的地址。 🔭阅读下面这两段代码: 🔭 指针数组: 字符指针数组 - 数组 - 存放的是字符指针 整型指针数组 - 数组 - 存放的是整

    2024年02月09日
    浏览(30)
  • C语言进阶之指针的进阶

    指针的主题,我们在C语言初阶博客已经接触过了,我们知道了指针的概念: 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定的4/8个字节(32位平台/64位平台)。 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候

    2024年02月16日
    浏览(28)
  • 【C语言进阶篇】之指针进阶(一)

    【C语言进阶技巧】指针掌握之道:解密指针的奇妙世界(第一部)) ❤️博客主页: 小镇敲码人 🍏 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌞回来4天了,加油!!!🍎🍎🍎 💗当你的能力匹配不上你的梦想,当你需要实现的目标匹配不上你的圈子的时候,你就会出现错位

    2024年02月13日
    浏览(39)
  • 【C语言进阶(五)】指针进阶详解(上)

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 本篇文章将讲解以下几个方面内容: 字符指针 数组指针 指针数组 数组传参和指针传参 函数指针 在这之前先温故一下指针的概念

    2024年02月12日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包