C语言进阶5——指针的进阶

Posted 初学C语言者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言进阶5——指针的进阶相关的知识,希望对你有一定的参考价值。

指针的进阶(2)


前言

本文接着上文,继续学习指针进阶的知识点:

  • 数组参数、指针参数
  • 函数指针
  • 函数指针数组

4、数组参数、指针参数

首先对上一篇所学知识进行回顾复习,将知识点,画在一张图上就能清楚的对比相同点和不同点:

编程时,有时要把数组或者指针传给函数,函数的参数该如何设计?下面举例说明:

4.1 一维数组传参

一维数组接收参数的几种方式
void test(int arr[])//形参与实参的定义相同,用数组接收

void test(int arr[10])//形参与实参的定义相同,用数组接收,[]内数字可以不写

void test(int* arr)//形参定义指针来接受数组首元素的地址

void test2(int* arr[20])//形参与实参的定义相同,定义指针数组来接受参数

void test2(int** arr)//二级指针,数组里的元素都是指针,就是地址
                   //数组名也是地址,地址的地址用二级指针

//一维数组传参
int main()

	int arr1[10] =  0 ;//一维数组
	int* arr2[20] =  0 ;//指针数组
	test(arr1);
	test2(arr2);

4.2 二维数组传参

二维数组接收参数的几种方式
void test(int arr[3][5])//形参与实参的定义相同,定义数组收受参数

void test(int arr[][5])//形参与实参的定义相同。第1个[]内数字可以不写

void test(int arr[][])//错误的,第2个[]内数字必须写

void test(int *arr)//错误的,*arr对地址解引用,
				//arr是二维数组名,第一行地址,第一行数组的首元素地址,获得第一个元素0					
void test(int* arr[5])//错误的,这是一个数组,每一个元素是指针,就是地址

void test2(int(*arr)[5])//指针指向的数组,是一个一维数组,数组有5个元素

void test2(int** arr)//错误的,二级指针,存放一级指针的地址
                   

//二维数组传参
int main()

	int arr[3][5] =  0 ;//二维数组
	二维数组在传参数组名时,传递的是首元素a[0],即第一行数组的地址
	test(arr);


4.3 一级指针传参

void test(int* arr, int sz)

	for (int i = 0; i < sz; i++)
	
		printf("%d ", *arr++);
	

int main()

	int arr[10] =  1,2,3,4,5,6,7,8,9,10 ;
	int* pa = arr;//直接把数组名给指针即可
	int sz = sizeof(arr) / sizeof(arr[0]);
	test(pa, sz);
	return 0;

下面反过来考虑:
当知道一个函数的形参部分为一级指针的时候,在主函数中调用时,应该传什么参数?

void test(int* p)

int main()

	int a = 10;
	int* p = &a;
	int arr[10];
	//传参的几种形式,都是可以的
	test(&a);
	test(p);
	test(arr);

	return 0;

4.4 二级指针传参

//举例 1
void test(int** p)//二极指针,地址的地址

int main()

	int n = 10;
	int* p = &n;
	int** pp = &p;

	test(pp);
	test(&p);

//举例 2
void test(char** ch)

int main()

	char ch = 'w';
	char* pc = &ch;
	char** ppc = &pc;
	char* arr[5];
	char arr1[3][5];

	test(arr);
	test(ppc);
	test(&pc);
	test(arr1);//错误的,二维数组穿的是第一行的地址,就是第一行数组的地址
	//应该char (*p)[5],参数应该用数组指针传参,指向数组的指针
				
//举例 3
void test1(int(*p)[5])//数组指针,指针指向数组,这个数组有5个元素,每个元素是int类型

void test2(int(*p)[3][5])//指针指向整个二维数组

	*p//解引用后代表首行地址

int main()

	int arr[3][5];
	test1(arr);//二维数组传递的是第一行地址
	test2(&arr);//传递的是整个二维数组的地址,一般不会这样用

5、函数指针

通过与前面所学的知识进行类比,就能发现定义指针的奥秘了:

  • 数组指针——指向数组的指针
  • 函数指针——指向函数的指针
//举例1
void test()

	printf("hehe\\n");

int main()

	printf("%p\\n", test);//函数名就是地址
	printf("%p\\n", &test);
	
	return 0;

//举例2
int add(int x, int y)

	return x + y;

int test(char* ch)

int main()

	//int arr[10];
	//arr
	//&arr
	//int* p = arr;
	//int(*p)[10] = &arr;

	int(*pa)(int, int) = add;//就是函数指针变量,将函数的地址保存起来
	int ret = (*pa)(2, 3);//此处的*不起作用,有无都行
	/*int ret = add(2, 3);

	int ret = pa(2, 3);*/
	printf("%d\\n", ret);
	//int(*pc)(char*) = test;
	return 0;

int main()

	void(*p)();//这个p的类型函数指针类型,去掉p后,剩下的是void(*)(),指针p指向的地址是一个参数是空,返回类型是空的函数

	//代码1
	(*void(*)()0)();
	解释说明:
	void(*)()是函数指针类型
	(void(*)()0,将0强制转换成函数指针类型,则指针0指向的地址里有一个函数
	*void(*)()0 地址解引用,就是函数名了
	(*void(*)()0)(),调用0地址处放的函数*/

	//代码2
	void(*signal(int, void(*)()(int)))(int);
	解释说明:
	signal(int, void(*)()(int)) 函数两个参数,一个int,一个函数指针
	void(*)()(int)是一个函数指针类型,指向的函数的参数是int,返回值是空
	//如同函数声明 int add(int, int);
	//去掉函数名和参数后,剩下的int就是函数的返回类型
	void(*signal(int, void(*)()(int)))(int)中去除函数名 signal(int, void(*)()(int))后,
	只剩下 void(* )(int),	它就是函数的返回类型,返回的是函数指针类型
	
	//代码2也可以简化:
	typedef void(*pfun_t)(int);
	pfun_t signal(int, pfun_t);
	

6、函数指针数组

通过类比学习定义:

指针数组:
int* arr[10]; //数组的每个元素是int*

函数指针数组:
int(*parr1[5])();
上面去掉数组名parr[5]后,剩下int(*)(),就是函数指针类型,这个数组有5个元素,每个元素都是int(*)()类型

parr1 先和 [] 结合,说明 parr1是数组,数组的内容:是 int (*)() 类型的函数指针
即数组里的每个元素都是指针,即地址,每个地址都存有一个函数,对指针解引用就能调用函数

函数指针数组的用处:
一个简易计算器的函数:

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 menu()

	printf("**************************\\n");
	printf("****  1.add   2.sub   ****\\n");
	printf("****  3.mul   4.div   ****\\n");
	printf("****  0.exit          ****\\n");
	printf("**************************\\n");

可以发现,当出现多个函数时,调用过程时相似的,但是代码重复较多,是冗余的

//常规的写法非常繁琐
int main()

	int input = 0;
	int x = 0;
	int y = 0;
	int res = 0;
	do
	
		menu();
		printf("请选择 ==> ");
		scanf("%d", &input);
		switch (input)
		
		case 1:
			printf("请输入两个操作数 ==> ");
			scanf("%d %d", &x, &y);
			res = add(x, y);
			printf("ret=%d\\n", res);
			break;
		case 2:
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			res = sub(x, y);
			printf("ret=%d\\n", res);
			break;
		case 3:
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			res = mul(x, y);
			printf("ret = %d\\n", res);
			break;
		case 4:
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			res = div(x, y);
			printf("ret = %d\\n", res);
			break;
		case 0:
			printf("退出计算器\\n");
			break;
		default:
			break;
		
	 while (input);
	return 0;

代码改写为使用函数指针数组:

//函数指针数组的用法
int main()

	int input = 0;
	int x = 0;
	int y = 0;
	int res = 0;
	int(*paar[5])(int, int) =  0,add,sub,mul,div ;
	do
	
		menu();
		printf("请选择 ==> ");
		scanf("%d", &input);
		if (input==0)
		
			printf("退出计算器\\n");
		
		else if (input >= 1 && input <= 4)
		
			printf("请输入2个操作数:>");
			scanf("%d%d", &x, &y);
			res = (*paar[input])(x, y);//直接调用函数
			printf("%d\\n", res);
		
		else
		
			printf("选择错误,重新输入数字\\n");
				
	 while (input);
	return 0;


总结

指针进阶的内容较多,而且这些内容的名词都相似,如果不能牢固掌握,将会混淆这些知识点。第一遍学习不能完全掌握,要经常复习,温故而知新。

通过类比,可以有效地区分各种定义。下一篇继续学习指针进阶的内容。

以上是关于C语言进阶5——指针的进阶的主要内容,如果未能解决你的问题,请参考以下文章

C语言指针进阶第五站:函数指针!

C语言进阶学习笔记二指针的进阶(重点必看+代码演示+练习)

C语言指针进阶第二站:指针数组!

18/5/4 小数据池,编码的进阶

C语言进阶4——指针的进阶

C语言指针就应该这么学 - 指针的进阶篇