• 『C语言进阶』指针进阶(二)


    在这里插入图片描述
    🔥博客主页 小羊失眠啦
    🔖系列专栏 C语言
    🌥️每日语录无论你怎么选,都难免会有遗憾。
    ❤️感谢大家点赞👍收藏⭐评论✍️


    在这里插入图片描述

    前言

    在上篇指针进阶中,我们对字符指针、指针数组、数组指针以及数组传参和指针传参有了一定的了解,你以为指针进阶就只有这些内容嘛?不不不,接下来,小羊将继续完善指针进阶内容,坐好小板凳准备上课了~~~

    一、函数指针

    1.1 函数指针的定义

    函数指针,顾名思义,就是一个指向函数的指针
    上篇中我们学到

    整形指针是接收整形的地址
    字符指针是接收字符的地址
    数组指针是接收数组的地址

    那么函数有地址吗?函数名又表示什么呢?
    答案是:函数是有地址的,地址是函数名或者&函数名,解引用时,p和*p都可以

    #include
    
    int Add(int x, int y)
    {
    	return x + y;
    }
    
    int main()
    {
    	int a = 1, b = 2;
    	int c = Add(a, b);
    	printf("%d\n", c);
    	printf("%p\n", &Add);
    	printf("%p\n", &Add);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:

    3
    00007FF750191348
    00007FF750191348
    
    • 1
    • 2
    • 3

    由此可见,函数名也可以表示函数的地址

    所以问题来了,我们以数组指针为例:
    写一个指向int arr[10]数组的数组指针

    第一步:
    (*p)		//先确定是一个指针
    第二步:
    (*p)[10]	//确定指向的是一个有10个元素的数组
    第三步:
    int(*p)[10]	//确定该数组元素为int型
    第四步:
    int(*p)[10]=&arr;//将数组的地址赋值给数组指针
    //或者int(*p)[10]=arr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们照着数组指针的例子来写一个函数指针:指向int add(int x,int y)

    第一步:
    (*p)			//先确定是一个指针
    第二步:
    (*p)(int,int)	//确定指向的函数有两个参数
    第三步:
    int (*p)(int,int)	//确定该函数的返回类型
    第四步:
    int (*p)(int,int)=&add;//将函数的地址赋值给函数指针
    //等价于:int (*p)(int,int)=add;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.2 函数指针调用

    1.1中得到 &函数名==函数名 ,所以函数指针的解引用调用可以不写*,也可以不写*。
    既然我们现在知道函数指针是怎么写的,那么函数指针有什么用呢?
    数组指针可以用来访问数组,那函数指针当然也就是调用函数的了,
    我们还是以数组指针为例:

    #include
    int main()
    {
    	int arr[10] = { 1,2,3,4,5 };
    	int(*p)[10] = arr;
    
    	for (int i = 0; i < 5; i++)
    	{
    		printf("%d ", (*p)[i]);
    	}
    	printf("\n");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    那现在用函数指针调用函数:

    #include
    
    int Add(int x, int y)
    {
    	return x + y;
    }
    
    int main()
    {
    	int a = 1, b = 2;
    	int(*p1)(int, int) = &Add;
    	int(*p2)(int, int) = Add;
    	int tmp1 = Add(a, b);
    	int tmp2 = (*p1)(a, b);//写法一
    	int tmp3 = (p2)(a, b);//写法二
    	printf("tmp1=%d\ntmp2=%d\ntmp3=%d", tmp1, tmp2, tmp3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行结果:

    tmp1=3
    tmp2=3
    tmp3=3
    
    • 1
    • 2
    • 3

    1.3 有趣的代码

    一、

    (*(void(*)())0)();
    //先分解
    一、void(*)()是一个无参无返回类型的函数指针
    二、(void(*)())0是将0强制类型转换,0原本是int类型,被强制转换为void(*)()函数指针
    三、*void(*)()0)这是将0转换为函数指针后的解引用操作
    四、(*(void(*)()0)()此时0就是一个函数的地址,可以看成(*0)(),意思就是解引用一个函数地址并且调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二、

    void(*signal(int,void(*)(int)))(int);
    我先一步一步分解给你们看
    void(*)(int)
    signal(int,void(*)(int))
    signal(int,void(*)(int))
    void(*signal(int,void(*)(int)))(int)
    signal先和(int,void(*)(int))结合,说明它是一个函数,参数为int,void(*)(int),现在我们明确了它的函数名和参数,还需要知道它的返回类型,先看一个函数,int Add(int),这个函数的函数名是Add,参数是int,把函数名和参数去掉后就是返回类型,同样,把这里的函数名和参数去掉,void(*)(int)这个就是它的返回类型
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面代码是一个函数调用


    二、函数指针数组

    2.1 函数指针数组的定义

    函数指针数组,存放函数指针的数组,每一个元素都是函数指针类型

    先写出函数指针
    int(*p)(int,int)
    改成数组
    int(*p[10])(int,int)
    
    • 1
    • 2
    • 3
    • 4

    例:

    #include
    
    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(*pf1) (int, int) = Add;
    	int(*pf2) (int, int) = Sub;
    	int(*pf3) (int, int) = Mul;
    	int(*pf4) (int, int) = Div;
    	int (*arr[4])(int, int) = { Add,Sub,Mul,Div };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (int i = 0; i < sz; i++)
    	{
    		printf("%d\n", arr[i](7, 4));
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    2.2 实战操作

    用C语言制作简易计算器

    #include
    
    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, y = 0;
    	int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };
    	do 
    	{
    		menu();
    		printf("请选择:>");
    		scanf("%d", &input);
    		if (input >= 1 && input <= 4)
    		{
    			printf("请输入两个操作数:>");
    			scanf("%d %d", &x, &y);
    			printf("%d\n", arr[input](x, y));
    		}
    		else if (input == 0)
    		{
    			printf("退出计算机");
    			break;
    		}
    		else
    			printf("选择错误\n请重新选择:>\n");
    
    	} while (input);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    三、函数指针数组指针

    指向函数指针数组的指针,是一个指针,指针指向存放函数指针的数组,该数组成员都是函数指针

    先写出函数指针
    int (*p)(int,int)
    改写成函数指针数组
    int(*p[10])(int,int)
    最后写成函数指针数组指针
    int(*(*p)[10]))(int,int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例:

    #include
    
    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 (*pa)(int, int) = Add;//函数指针
    	int (*arr[4])(int, int) = { Add };//函数指针数组,存放函数指针的数组
    	int (*(*ppa)[4])(int, int) = &arr;//函数指针数组指针,存放函数指针数组地址的指针
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、回调函数

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

    示例:

    #include
    
    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, y = 0;
    	printf("请输入两个操作数:>");
    	scanf("%d %d", &x, &y);
    	int ret = pf(x, y);
    	printf("%d\n", ret);
    }
    
    int main()
    {
    	int input = 0;
    	do
    	{
    		menu();
    		printf("请选择:>");
    		scanf("%d", &input);
    		switch (input)
    		{
    		case 0:
    			printf("退出计算机\n");
    			break;
    		case 1:
    			Calc(Add);
    			break;
    		case 2:
    			Calc(Sub);
    			break;
    		case 3:
    			Calc(Mul);
    			break;
    		case 4:
    			Calc(Div);
    			break;
    		default:
    			printf("输入错误\n");
    			break;
    		}
    	} while (input);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    4.1 排序

    冒泡排序,相邻两个两个的比较并交换位置,在C语言初阶数组中,详细讲解了关于冒泡排序的知识点,忘记了的铁汁们可以看一下
    代码展示:

    #include 
    void Print(int arr[], int sz)
    {
    	for (int i = 0; i < sz; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    }
    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 - i - 1; j++)
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				int tmp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = tmp;
    			}
    		}
    	}
    }
    int main()
    {
    	int arr[] = { 5,3,7,6,1,8,9,2,4,0 };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	bubble_sort(arr,sz);
    	Print(arr, sz);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    4.2 qsort部分展示

    冒泡排序只能排序整形,而qsort函数,内部采用快速排序,可以排序各种类型的数据,接下来展示qsort排序部分类型的方法.

    qsort是一个库函数,快速排序的方法来实现的, 头文件是
    qsort库函数,void qsort( void *base, size_t num, size_t width, int (_cdecl *compare )
    (const void *elem1, const void *elem2 ) );传入的参数,一个是指针,一个整形,一个整形一个函数指针,base 数组首元素(就是数组名),num数组里有多少个元素,width每个元素的大小(单位是字节),compare比较两个指针指向的元素,小于 输出小于0的元素,等与 输出0,大于 输出大于0的元素 排序任意类型

    示例:
    qsort函数部分应用
    分别将元素比较方法int_cmp和char_cmp的指针(地址) 传给 qsort函数.由qsort函数调用这些比较函数

    #include 
    #include 
    int int_cmp(const void* e1, const void* e2)//整形元素排序方法
    {
    	return *(int*)e1 - *(int*)e2;
    }
    int char_cmp(const void* e1, const void* e2)//字符型元素排序方法
    {
    	return *(char*)e1 - *(char*)e2;
    }
    int main()
    {
    	int arr1[10] = { 4,5,1,8,9,2,10,3,7,6 };
    	char arr2[] = "fbadegc";
    	int sz1 = sizeof(arr1) / sizeof(arr1[0]);
    	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
    	qsort(arr1,sz1,sizeof(arr1[0]),int_cmp);
    	for (int i=0; i < sz1; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	printf("\n");
    	qsort(arr2, sz2, sizeof(arr2[0]), char_cmp);
    	for (int i = 0; i < sz2; i++)
    	{
    		printf("%c ", arr2[i]);
    	}
    	printf("%s", arr2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行结果:

    1 2 3 4 5 6 7 8 9 10
     a b c d e f g
    
    • 1
    • 2

    qsort函数用冒泡排序的模拟实现,以及各种类型的排序咱们下篇一起学习~
    希望这篇文章对铁汁们有所帮助,咱们下期再见!

    在这里插入图片描述

  • 相关阅读:
    LeetCode24.两两交换链表中的节点
    ovn metadata (by quqi99)
    Java 面试题之框架
    hive笔记(四):查询、分组-运算符/limit/where/like/rlike/group by/having
    Go-变量& 常量
    大厂面试总结大全二
    如何在Ubuntu上安装WordPress
    Codeforces暑期训练周报(8.22~8.28)
    Jmeter常用断言之断言持续时间简介
    企业IT信息化三阶段:追随、协同,到引领
  • 原文地址:https://blog.csdn.net/hsjsiwkwm/article/details/132942270