C语言 指针的进阶(二):常量指针和指针常量、数组参数和指针参数、函数指针数组

指针的进阶(二)目录:


常量指针和指针常量

在我们日常中,经常会用到一个关键字const

const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。

合理的运用const可以大幅度的提高代码的安全性和可读性。
但是在指针中,const该如何运用呢?
在这里插入图片描述
对于上面两种情况,大多数人很容易就把第一个指针当成了常指针,但是却并非如此。
第一个其实是一个,第二个才是常量指针。
定义时const位于 * 前面是常量指针,const位于*后面是指针常量。

1.常量指针

即指向常量的指针
在这里插入图片描述
因为它指向的对象是一个常量,所以该指针的值无法修改,但是可以修改它指向的对象
在这里插入图片描述
编译器提示我们不能进行修改。
在这里插入图片描述
而当我们去修改它所指向的对象的时候,编译就通过了。

2.指针常量

在这里插入图片描述
这个就与上面的刚好相反的,这个指针本身就是一个常量,所以不能改变它所指向的对象,但我们能够改变指向对象的数值。
在这里插入图片描述
在这里插入图片描述
我们试图去修改它所指向的对象,编译不通过。
在这里插入图片描述
我们试图去修改它所指向的对象的值,编译通过。

指向常量的指针常量

在这里插入图片描述
指向常量的指针常量其实就是将上述的两个结合了起来
当我们既不想让别人修改我们指向的对象,也不想让别人修改指向对象的值时,我们就可以用这个来实现。


数组参数和指针参数

在我们写代码的时候经常会把指针和数组作为参数传给数组,那函数的参数该如何设计呢?

一维数组传参

假设我们存在一个大小为10的数组,当我们将其作为参数传给函数时
在这里插入图片描述
会惊奇的发现,当我们不填数组元素个数时,我们传参成功了。当我们填写的元素个数大于数组大小时,传参竟然也成功了。
同时我们在函数中用sizeof来判断数组大小时,我们得到的结果是4。
结合这两点,我们发现,C语言传参时为了效率和节约空间,并不会直接传整个数组,而是转为传指针,指针指向数组首元素的地址,这又就是为什么我们无论下标中填写多少都没有问题的原因。
所以我们传递参数的形式其实应该是这样
在这里插入图片描述


二维数组传参

当我们在传递二维数组的时候,如果还像刚刚一样,就会出现问题了
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191110194548757.png在这里插入图片描述
我们发现了一个问题,上面的三个只有中间编译通过,所以二维数组并不能完全省略下标的内容,第二个下标中必须要填写数字,这又是为什么呢?
我们对arr进行+1,查看它地址的变化
在这里插入图片描述
在这里插入图片描述
我们发现它+1加了20,是一列的大小。

在第一节中我说过,指针的类型决定了它如何看待这个地址,而它加一加了一列的大小,说明它指向的类型应该是一个数组,数组的大小为列的元素。
所以在这里,我们传进去的参数就应该是一个数组指针,而了解到这一点后,我们也会发现,数组名代表的首元素地址就是第一行的地址。

所以它的本质应该是
在这里插入图片描述


指针传参

指针的传参就相对来说简单一点了,在这里就不太详细的介绍。

一级指针
#include<stdio.h>

void print(int *p, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		printf("%d ", *(p + i));;
	}
}
int main()
{
	int arr[5] = {1, 2, 3, 4, 5};
	int *p = arr;
	int size = sizeof(arr) / sizeof(arr[0]);
	print(p,size);
	return 0;
} 

在这里插入图片描述
当指针作为参数时,我们只需要对其进行解引用,就可以操作它所指向的对象。

二级指针
#include<stdio.h>

void print(int **p)
{
	printf("%d\n", **p);
}
int main()
{
	int n = 10;
	int *p = &n;
	int **pp = &p; 
	print(pp);
	print(&p);
	return 0;
} 

在这里插入图片描述
同理。


函数指针数组

大家在接触指针的时候经常会听到一些名词,数组指针数值,函数指针数组,函数指针数组指针这种一环套一环的令人头大的东西。

其实按照我们上一节一开始介绍的方法很容易就可以看出来。

如:在这里插入图片描述
先看括号内的优先级,星号的优先级比方括号低,所以这个东西的本质就时一个数组,然后剩下的就是它所存放的数据的类型,然后第二优先级是星号,所以他存放的是一个指针,剩下的就是它所指向的数据的类型,所以它保存的元素是数组指针,所以它是一个数组指针数组。

同理 :在这里插入图片描述
这样的就是函数指针数组。

而这个看起来像
在这里插入图片描述
但他其实是一个函数的声明,其返回值为指针数组。

那么函数指针数组的作用是什么呢?它最大的作用就是可以简化代码,减少很多重复的不必要的代码。
例如下面这段代码

int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			breark;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

在switch中有很多完全重复的代码,如果我们用函数指针数组优化后是这样的。

int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

这样就大大减少了代码量。

发布了60 篇原创文章 · 获赞 78 · 访问量 6322

猜你喜欢

转载自blog.csdn.net/qq_35423154/article/details/102998017