• 进阶C语言-指针的进阶(中)


    在这里插入图片描述

    📖5.函数指针

    数组指针 - 指向数组的指针 - 存放的是数组的地址 - &数组名就是数组的地址。
    函数指针 - 指向函数的指针 - 存放的是函数的地址 - &函数名就是函数的地址。

    #define _CRT_SECURE_NO_WARNINGS 1
    #include 
    int Add(int x, int y)
    {
    	return x + y;
    }
    int main()
    {
    	//&函数名就是函数的地址
    	//函数名也是函数的地址
    	printf("%p\n", &Add);
    	printf("%p\n", Add);
    	int (*pd1)(int, int) = &Add;
    	//int (*pd2)(int, int) = Add;//pd2是函数指针变量
    	int ret = (*pd1)(3, 5);
    	printf("%d\n", ret);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述
    🔭阅读下面这两段代码:

    //代码1:
    (*(void (*)())0)();
    //void (*)()为函数指针类型
    //下面这段代码是在调用0地址处的函数,这个函数没有参数,返回类型是void
    //代码2:
    void (*signal(int , void(*)(int)))(int);
    //signal函数的参数有两个,第一个是int类型,第二个是函数指针类型,该类型是void(*)(int),该函数指向的类型是int,返回类型是void.signal函数也是函数指针类型,该类型是void(*)(int),该函数指向的类型是int,返回类型是void.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    📖6.函数指针数组

    🔭 指针数组:
    字符指针数组 - 数组 - 存放的是字符指针
    整型指针数组 - 数组 - 存放的是整型指针
    那么函数指针数组 - 数组 - 存放的是函数指针 - 存放的是函数的地址。

    #include 
    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) = ⋐
    	int (*pfArr[4])(int, int) = { &Add,&Sub };
    	//pfArr是函数指针数组,是存放函数指针的数组 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    🔎这里我们来模拟一下计算器的实现:

    #define _CRT_SECURE_NO_WARNINGS 1
    #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;
    	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("ret = %d\n", ret);
    			break;
    		case 2:
    			printf("请输入2个操作数:");
    			scanf("%d %d", &x, &y);
    			ret = Sub(x, y);
    			printf("ret = %d\n", ret);
    			break;
    		case 3:
    			printf("请输入2个操作数:");
    			scanf("%d %d", &x, &y);
    			ret = Mul(x, y);
    			printf("ret = %d\n", ret);
    			break;
    		case 4:
    			printf("请输入2个操作数:");
    			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;
    }
    
    • 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
    • 72
    • 73

    ✅自测运行:
    在这里插入图片描述
    🎈程序不足:如果想要实现更多的功能||、&&、&、|、>>、<<等等,还得重复写一些步骤,就很冗余!
    🏆这里,我们就可以利用函数指针数组的知识来优化它,函数指针数组的作用:转移表!

    #define _CRT_SECURE_NO_WARNINGS 1
    #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;
    	int y = 0;
    	int ret = 0;
    	do
    	{
    		menu();
    		printf("请选择:>");
    		scanf("%d", &input);
    		//函数指针数组-转移表
    		int (*pfarr[])(int, int) = { NULL,Add,Sub,Mul,Div };
    		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;
    }
    
    • 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

    在这里插入图片描述

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

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

    如何定义?

    void test(const char* str)
    {
    printf("%s\n", str);
    }
    int main()
    {
    //函数指针pfun
    void (*pfun)(const char*) = test;
    //函数指针的数组pfunArr
    void (*pfunArr[5])(const char* str);
    pfunArr[0] = test;
    //指向函数指针数组pfunArr的指针ppfunArr
    void (*(*ppfunArr)[5])(const char*) = &pfunArr;
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    📖8.回调函数

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

    🔎这里,我们利用回调函数实现一下计算器:

    #define _CRT_SECURE_NO_WARNINGS 1
    #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;
    	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;
    }
    
    • 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

    ✅自测运行:
    在这里插入图片描述
    🔎这里,我们演示一个qsort函数的实现:

    qsort函数是一个库函数,底层使用的是快速排序的方式,对数据进行排序的这个函数可以直接使用,这个函数可以用来排序任意类型的数据。

    🌞我们先一起回顾一下冒泡排序的内容:

    //冒泡排序算法:
    //给一组整型数据,然后使用冒泡排序算法,对数据进行升序排列
    //冒泡排序的思想:两两相邻的元素进行比较
    #include 
    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;
    			}
    		}
    	}
    }
    void print_arr(int *arr,int sz)
    {
    	int i = 0;
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	printf("\n");
    }
    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;
    }
    
    • 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

    在这里插入图片描述

    🎈缺陷:我们发现这个函数不够通用,只能排序整型类型!
    📝关于qsort函数,我们可以先去cpluplus网站上面了解一下:

    在这里插入图片描述

    //1.排序整型数组,两个整型可以直接使用大于号比较。
    //2.排序结构体数组,两个结构体数据可能不能直接使用大于号比较。
    //不同类型的数据,比较出大小,方法是有差异的。
    void qsort (void* base//待排序数组的第一个元素的地址
    ,           size_t num//待排序数组的元素个数
    ,           size_t size//待排序数组中一个元素的大小
    ,           int (*compar)(const void* e1,const void* e2));//函数指针compar指向了一个函数,这个函数是用来比较两个元素的
    //e1和e2中存放的是需要比较的两个元素的地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    🔭关于void*类型的指针:

    1. void*类型的指针 - 不能进行解引用操作符,也不能进行±整数的操作
    2. void*类型的指针是用来存放任意类型数据的地址
    3. void*无具体类型的指针
    #include 
    int int_cmp(const void* p1, const void* p2)
    {
    	return (*(int*)p1 - *(int*)p2);
    }
    void _swap(void* p1, void* p2, int size)
    {
    	int i = 0;
    	for (i = 0; i < size; i++)
    	{
    		char tmp = *((char*)p1 + i);
    		*((char*)p1 + i) = *((char*)p2 + i);
    		*((char*)p2 + i) = tmp;
    	}
    }
    void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
    {
    	int i = 0;
    	int j = 0;
    	for (i = 0; i < count - 1; i++)
    	{
    		for (j = 0; j < count - i - 1; j++)
    		{
    			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
    			{
    				_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
    			}
    		}
    	}
    }
    int main()
    {
    	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    	//char *arr[] = {"aaaa","dddd","cccc","bbbb"};
    	int i = 0;
    	bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
    	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	printf("\n");
    	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

    好啦,关于进阶指针(中)的知识点到这里就结束啦,后期会继续更新C语言的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️

  • 相关阅读:
    基于Python贝叶斯优化XGBoost算法调参报错“TypeError: ‘float‘ object is not subscriptable”
    17、负载均衡
    零基础学前端(三)重点讲解 HTML
    Docker - 容器的网络模式
    视频剪辑中生成花字特效的代码案例详述
    css问题
    深度学习网路模型
    ID生成器代码重构问题
    【长难句分析精讲】定语从句
    【原型设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
  • 原文地址:https://blog.csdn.net/qq_73121173/article/details/132787607