指针的进阶(二)目录:
常量指针和指针常量
在我们日常中,经常会用到一个关键字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;
}
这样就大大减少了代码量。