指针进阶!关于指针、数组、函数的多重套娃理解


目录

前言

指针的概念是什么?

指针与数组、函数的多重套娃

指针可以接受的哪些类型

回调函数的概念与理解


前言:理解好指针与数组、函数的多重结合,再利用回调函数可以帮助我们更好的去使代码更加简洁,并且在后期的学习中,回调函数是非常重要的,在进阶冒泡排序中也会用到回调函数。

指针的概念是什么?

//指针的概念:
//1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间
//2.指针的大小是固定的4/8个字节(32位平台/64位平台)
//3.指针是有类型的,指针的类型决定了指针的+/-这还能数的步长,指针解引用操作的时候的权限。
//4.指针的运算

 

指针与数组、函数的多重套娃

int main()
{
	char arr[] = "abcdef";
	char *pc = arr;//a的地址放进pc里去
	printf("%s\n", arr);
	printf("%s\n", *pc);//pc里装的是arr首元素的地址,但我们如果打印字符串的话,这里需要从首地址开始打印直到遇到\0,此时我们注意的是在这里pc存的是
	return 0;//首元素地址,如果我们加了解引用后,相当于不是地址,而是a,那么此时在打印字符串时候是不行的,打印字符串不能加*,且pc指向的是首元素的
}//地址

 常量字符串的打印

int main()
{
	char *p = "abcdef";//"abcdef"是一个常量字符串,就是把a的地址赋给了p,而不是将字符串“abcdef”放入字符指针p中,通过这个首地址就能找到整个字符串
	//的空间,如果我们在char前加const:此时这个指针字符指向的内容不允许被修改
	printf("%c\n", *p);//与上个代码相比,是把这个字符串的首字符的地址赋给p,p里存的是a的地址,*p就是a
	printf("%s\n", p);//p里存的是a的地址,但我们打印的是字符串,于是从该地址处打印字符串,直接遇到\0结束,这个与上面代码的第二个打印一样,如果我们
	return 0;//加了*后,p代表的不是首元素地址,而是解引用后的a,那么此时无法往后面打印字符串。
}
{
	int arr[10] = { 0 };//整型数组
	char ch[5] = { 0 };//字符数组
	int* parr[4];//指针数组-整形指针的数组或者存放整形指针的数组:这个数组有四个类型为int*的元素
	char* pch[5];//存放字符指针的数组-指针数组,五个元素类型是char*
	return 0;
}

 一级指针和一维数组的介绍

int main()
{
	int *p = NULL;//整型指针->指向整型的指针
	char* p = NULL;//字符指针->指向字符的指针-可以存放字符的地址
	//数组指针-指向数组的指针->存放数组的地址
	int arr[10] = { 0 };
	//arr-首元素的地址
	//&arr[0]-首元素的地址
	//&arr-数组的地址
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //如何分辨是数组还是指针:[]的优先级比*高:int*p[10]是数组,如果是int(*p)[10]就是指针
	int (*p)[10] = &arr;//数组的地址要存起来:此时p是个指针,用来存放数组的地址
	return 0;
}

 关于字符/整型指指针和数组的介绍

int main()
{
	char *arr[5];
	char* (*pa)[5] = &arr;//首先(*pa)是一个指针,(*pa)[5]是一个数组指针:指向一个具有五个元素的数组
	//char*(*pa)是这个(*pa)指针指向一个数组,这个数组有五个元素,且每个元素的类型是char*
	int arr2[10] = { 0 };
	int (*pa2)[10] = &arr2;
	return 0;
}

 这里是对二维数组传参的说明,既可以传入一个二维数组中,也可以传入一个数组指针中

void print1(int arr[3][5], int x, int y)//参数是数组的形式
{
	
	int i = 0;
	int j = 0;
	for (i = 0; i < x; i++)
	{
		
		for (j = 0; j < y; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print2(int(*p)[5], int x, int y)//参数是指针的形式
{
	int i = 0;
	int j = 0;
	for (i = 0; i < x; i++)
	{
		
		for (j = 0; j < y; j++)
		{
			//printf("%d ", p[i][j]);
			printf("%d ",*(p[i]+j));
			//printf("%d ", *(*(p + i) + j));
			//printf("%d ", (*(p + i))[j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { { 1, 2, 3 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7, } };
	print1(arr, 3, 5);//arr-数组名-数组名就是首元素地址-我们首先把二维数组想象成一维数组,第一行就是第一个元素,那么我们传参时,这个首元素的地址
	//就是第一行的地址
	print2(arr, 3, 5);

	return 0;
}

 这里介绍的是数组,指针,数组指针,指针数组。有一个诀窍是:我们先判断parr是数组还是指针,判断完之后再去掉该部分,剩下的就是数组的元素类型或者指针指向的该数组的地址。

int arr[5];--arr是一个有五个元素的数组 
int * parr1[10];--parr1是一个数组,该数组有十个元素,每个元素的类型是int*,parr1是指针数组
int(*parr2)[10];--parr2是一个指针,该指针指向一个数组,数组有10个元素,每个元素的类型是int-parr2是数组指针
int(*parr3[10])[5]--parr3是一个数组,该数组有十个元素,每个元素是一个指针,该指针指向的数组的元素有五个元素,每个元素是int
int main()
{
	char ch = 'w';
	char*p = &ch;
	const char *p2 = "abcdef";//这里注意p是个字符指针,只能存放一个字节四个字节,无法存放“abcdef”,其实是把这个字符串的首元素的地址赋值给p2
	//const放在左边的话,修饰的是*p2,也就是p2指向的内容不可被修改
	//指针数组-数组-存放数指针的数组
	int *arr[10];//这个数组有十个元素,每个元素是一个指针,类型为int*
	char* ch[5];
	//数组指针-指向数组的指针
	//int* p3;//整型指针-指向整型的指针
	//char* p4;//字符指针-指向字符
	int arr2[5];
	int (*pa)[5] = &arr2;//取出整个数组的地址,由一个指针存放,int(*pa)[5]是一个指针指向一个有五个元素的数组,每个元素又是int
	return 0;
	
}

回调函数的概念与理解

这里是一个关于设计计算器的代码,里面运用到了回调函数与函数指针的多重套娃,大家可以对这个代码进行debug一下就可以很清晰的明确这个多重嵌套。最主要的是我们定义一个指针指向一个数组,而每个数组的元素又是一个函数指针,这个函数指针找到我们五个加减乘除和按位与五种函数地址进行调用。

void menu()
{
	printf("***************************\n");
	printf("********  1.add.  2.sub ***\n");
	printf("********  3.mul.  4.div ***\n");
	printf("*****   5.xor  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 xor(int x, int y)
{
	return x^y;
}
//回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,这就是回调函数
void calc(int(*pf)(int,int))//这个calc函数里的参数是个函数指针接收下面calc的函数地址,接着调用
{
	int x = 0;
	int y = 0;
	printf("请输入两个操作数:>");
	scanf("%d%d", &x, &y);
	printf("%d\n", pf(x,y));
}
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 5:
				calc(xor);
		case 0:
			printf("退出\n");
			break;
		default:
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;//取出数组的地址

	int(*pf)(int, int);//函数指针
	int(*pfarr[4])(int, int);//pfarr是一个数组-函数指针的数组,由这个得到下一个:指向函数指针数组的指针就是给pfarr加上*再括起来,此时变成了一个指针,存放的地址就是pfarr
	int(*(*ppfarr)[4])(int, int) = &pfarr;
	//*ppfarr是一个数组指针,指向的是一个具有四个元素的数组,每个元素的类型就是表达式去掉(*ppfarr)后的表达式:int(* )(int ,int)
}//这个类型就是函数指针:int(*)(int,int)

 这里也是一个回调函数,我们首先在主函数中调用test函数将print函数传参,用*p这个函数指针来接收print函数的地址,然后开始执行print函数,打印test之后调用指针p将bit这串字符串传入print函数中去,函数中的参数用一个字符指针来接收最终在print函数中打印出我们要的结果。

在这个代码中,不难看出,我们没有直接调用了这个print函数,而是在中间又插入一个函数指针去作为媒介来进行回调,这就是回调函数的核心。

print(char* str)
{
	printf("hehe:%s", str);
}
void test(void (*p)(char*))//这里写的是函数指针来接收下面test(print)中print函数的地址
{
	printf("test\n");
	p("bit");//p指向这个函数,然后再通过p来找到这个print函数,
此时p中的这个bit传入print函数里去,被字符指针char*接收,在打印
}
int main()
{
	test(print);//我们刚开始通过test函数将print函数的地址传进p指针里去
	return 0;
}//这种机制就叫做回调函数,回调函数往往不是直接调用,而是通过中间媒介去调用

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值