前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【进阶指针二】数组传参&指针传参&函数指针&函数指针数组&回调函数

【进阶指针二】数组传参&指针传参&函数指针&函数指针数组&回调函数

作者头像
MicroFrank
发布2023-01-16 11:54:40
7550
发布2023-01-16 11:54:40
举报

【进阶指针一】字符指针&指针数组&数组指针

https://blog.csdn.net/qq_64428099/article/details/125011487

目录

 1.数组传参

1-2 二维数组传参 

2 指针传参

2-1 一级指针传参

2-2 二级指针传参

2-3 关于传&arr和arr

3 函数指针

3-1 函数指针的引入

3-2 函数指针的脱裤子放屁使用【先见一见基本操作】

3-3 试图看懂大佬写的代码

 4 函数指针数组

4-1函数指针数组的引入和基本使用

 4-2 函数指针数组的妙用


 1.数组传参

1-1一维数组传参

正向:实参给定,猜猜看形参可以怎么写?

测试1:

注:这里没有要求打印,所以没有传元素个数sz

 测试2:

void test2(int* arr2[10])//bingo
void test2(int* arr2[])//bingo
void test2(int** arr2)//bingo

int main()
{
	int* arr2[10] = { 0 };
	test2(arr2);//数组名是首元素(int*类型)的地址
	return 0;
}

1-2 二维数组传参 

void test(int arr[3][5])//bingo-列必须写上,且必须写对
void test(int arr[][5])//bingo -敲黑板!!!行可以省略,但是列不可以省略
void test(int arr[][])//error -列必须写上,且必须写对

void test(int(*p)[5])//bingo -数组指针,指针指向的是一个一维数组



int main()
{
	int arr[3][5] = { 0 };
	test(arr);//数组名是首元素(int [5]类型)的地址
	return 0;
}

关于我对二维数组及多维数组的理解: 

 由二维数组引申的两个普遍规律: 1. 我们所知的复合类型(比如数组,结构体等)的类型名都是首元素的地址(两个特殊情况除外)。 2. 对于多维数组定义或者传参时,只有第一维数组的数组元素可以省略,其余维必须写上,且必须写对!!!

2 指针传参

反向:形参给定,猜猜看实参可以怎么写?

2-1 一级指针传参

void test1(int* ptr)//一级指针:存放普通变量的地址
{
	//...
}

int main()
{
	int a = 10;
	int* p = &a;

	test1(&a);
	test1(p);//p是一级指针变量的内容

	int arr[10];
	test1(arr);

	return 0;
}

2-2 二级指针传参

void test2(int** pp)//二级指针:存放一级指针的地址
{
	//...
}

int main()
{
	char ch = 'a';
	char* pch = &ch;
	char** ppch = &pch;
	
	test2(&pch);
	test2(ppch);

    //错误范例1:
    test2(&&ch);ch的地址(数据)是没有地址的,只有ch的地址被变量存起来(变量)才有地址

	char* arr[5];
	test2(arr);//数组名是首元素(也就是一级指针变量)的地址


    //错误范例2:
    char arr2[3][5];
    test2(arr);//error!!!!// arr是二维数组数组名,表示的是一维数组的地址
   
	return 0;
}

2-3 关于传&arr和arr

2-3-1 这里以二维数组为例,讲一讲实参和形参的匹配问题

void test1(int(*p)[5])// int[5]*类型
{
	;
}

void test2(int(*p)[3][5]) //int[3][5]*类型
{
	;
}

int main()
{
	int arr[3][5];
	test1(arr);//二维数组首元素(整个一维数组)的地址
	test2(&arr);//整个二维数组的地址
	return 0;
}

这里以一维数组为例,讲一讲函数内要想打印的具体实现(&arr的鸡肋问题): 如果在主函数调用的时候传&arr的话就太鸡肋了!(因为你传整个数组的地址,你又不能一次性打印出来,你还得对整个数组的地址进行解引用。 解引用后就是一维数组的数组名,因为这个数组名不是那两个特殊情况,所以这个数组名又摇身一变,变成数组首元素的地址,到这里就和直接在主函数调用的时候传arr的效果是一样的)

void Print1(int* arr,int n)
{
	printf("Print1:>\n");
	for (int i = 0; i < n; i++)
	{
		printf("arr[%d]=%d\t", i, *(arr + i));
	}
}

void Print2(int(*arr)[3], int n)
{
	printf("Print2:>\n");
	for (int i = 0; i < n; i++)
	{
		printf("arr[%d]=%d\t", i, *(*arr + i));//鸡肋
	}
}

int main()
{
	int arr[3] = { 1,2,3 };

	Print1(arr,3);
	printf("\n\n");
	Print2(&arr,3);
	return 0;
}

3 函数指针

函数是放在代码区的,只要是定义了就会在编译阶段就会分配空间,和全局变量一样。

3-1 函数指针的引入

int Add(int a, int b)
{
	return a + b;
}



int main()
{
	//函数也有地址
	printf("%p\n", Add);
	printf("%p\n", &Add);//-取地址只是摆设捏

	//有地址能不能用能不能用指针变量存起来-函数指针

	int (*p1)[3];//-数组指针
	int(*p2)(int,int) = &Add;//-函数指针,指向函数的指针
	printf("%p\n", p2);

	return 0;
}

[]和()的运算符优先级都比*高 

关于为什么要有函数参数的一点思考: -这和数组指针类似,int(*p)5,这个5是对指针指向的内容的必要说明,也就是所指向数组类型的一部分,不可省略。 -同理,函数指针的类型里的返回值和形参都是对所指向函数的必要说明。

    char* test(int(*p)[5], char*)
    {
	  return NULL;
    }

	//问题:来照猫画虎写一个指向这个函数的函数指针
	//答案:char*(*p)(int(*)[5], char*) = &test;

3-2 函数指针的脱裤子放屁使用【先见一见基本操作】

int Add(int a, int b)
{
	return a + b;
}


int main()
{
	printf("test1:\n%p\n", &Add);
	printf("test2:\n%p\n", Add);

	int(*p)(int, int) = &Add;
	//int(*p)(int, int) = Add;
	
	printf("test3:\n%d\n", Add(2, 3));
	printf("test4:\n%d\n", (*p)(2,3));
	printf("test5:\n%d\n", p(2,3));
	//在获得函数地址时,&和不加&都可以,但是加上&更好理解
	//在通过调用函数时, *和不加*都可以,但是加上*更好理解,且必须要带上括号

	return 0;
}

3-3 试图看懂大佬写的代码

代码1:

(*(void(*)())();
//提示:这个整体是函数调用

 子例程:函数

参考:《C陷阱和缺陷》

 代码2:

void(* signal(int,void(*)())(int);
//提示:这个整体是函数声明

小小勘误:图片中第3步中指针类型应该改为函数指针类型 

 4 函数指针数组

4-1函数指针数组的引入和基本使用 只要你前面学会了,这里就是一样的套用,我这里就不啰嗦了

int Add(int a, int b)
{
	return a + b;
}

int Sub(int a, int b)
{
	return a - b;
}

int Mul(int a, int b)
{
	return a * b;
}

int Div(int a, int b)
{
	return a / b;
}


int main()
{
	//字符指针数组
	char* arr1[5];
	//整型指针数组
	int* arr2[5];

	//函数指针数组
	//int(*pf[4])(int, int);

	//在没有函数指针数组之前...
	int(*pf1)(int, int) = Add;
	int(*pf2)(int, int) = Sub;
	int(*pf3)(int, int) = Mul;
	int(*pf4)(int, int) = Div;

	//有函数指针数组之后...
	int(*pf[4])(int, int) = { Add,Sub,Mul,Div };
	for (int i = 0; i < 4; i++)
	{
		int ret=pf[i](6, 2);
		printf("%d\n", ret);
	}

	return 0;
}

 函数指针数组的优缺点:优点:不用一个一个定义变量去存储函数的地址,然后一个一个去调用 缺点:函数指针数组既然是数组,就要求是相同类型元素的集合,也就是返回值和参数类型的一样才能放到函数指针数组内,统一进行操作。

 4-2 函数指针数组的妙用

函数指针数组实现加减乘除运算器,这里的函数指针数组被称为转移表

int Add(int a, int b)
{
	return a + b;
}

int Sub(int a, int b)
{
	return a - b;
}

int Mul(int a, int b)
{
	return a * b;
}

int Div(int a, int b)
{
	return 1.0*a / b;
}

void meau(void)
{
	printf("**************************\n");
	printf("******     1.Add     *****\n");
	printf("******     2.Sub     *****\n");
	printf("******     3.Mul     *****\n");
	printf("******     4.Div     *****\n");
	printf("******     0.exit    *****\n");
	printf("**************************\n");
}

int main()
{
	int input = 0;
	int operand1 = 0;
	int operand2 = 0;
	int(*pf[5])(int, int) = {0,Add,Sub,Mul,Div};
	meau();
	do
	{
		printf("请输入你的选择:>");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出程序\n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d%d", &operand1, &operand2);
			printf("%d\n", pf[input](operand1,operand2));
		}
		else
		{
			printf("输入非法,请重新输入\n");
			continue;
		}

	} while (input);
	return 0;
}

5 回调函数

回调函数:把函数1的地址作为函数2的函数参数,从而调用函数2,然后再函数2实现过程中通过指针调用函数1,那么这个被其他函数调用的函数(函数1)就被称为回调函数。

void test1(void(*p)())
{
	(*p)();
}

void test2()
{
	printf("test2\n");
}
int main()
{
	test1(&test2);
}

5-1 回调函数的使用举例1:计算器 原来的switch  case 语句好多冗余的语句,又有前提减加乘除的函数参数和返回值类型相同,所以可以使用回调函数处理这个问题。

void cal(int (*p)(int, int))
{
	int o1 = 0;
	int o2 = 0;
	printf("请输入两个操作数:>");
	scanf("%d%d", &o1, &o2);
	printf("%d\n", p(o1, o2));
}

int main()
{
	int input = 0;

	meau();
	do
	{
		printf("请输入你的选择:>");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			cal(Add);
			break;

		case 2:
			cal(Sub);
			break;

		case 3:
			cal(Mul);
			break;

		case 4:
			cal(Div);
			break;

		case 0:
			printf("exit\n");
			break;

		default:
			printf("非法\n");
		}

	} while (input);
	return 0;
}

这里的Add,Sub,Mul,Div函数都是回调函数,通过传不同函数的地址给Cal函数,Cal函数内部用函数指针接收,从而实现了Cal函数的多重功能。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  •  1.数组传参
    • 1-2 二维数组传参 
    • 2 指针传参
      • 2-1 一级指针传参
        • 2-2 二级指针传参
          • 2-3 关于传&arr和arr
          • 3 函数指针
            • 3-1 函数指针的引入
              • 3-2 函数指针的脱裤子放屁使用【先见一见基本操作】
                • 3-3 试图看懂大佬写的代码
                •  4 函数指针数组
                  •  4-2 函数指针数组的妙用
                  • 5 回调函数
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档