二级指针
二级指针是用来存放一级指针地址。
如何使用和解引用呢?
#include <stdio.h>
int main()
{
int a = 5;
int* p = &a;
int** p2 = &p;
**p2 = 10;
printf("%d\n", a);
return 0;
}
这里的解引用使用两颗星号的原因是:一个星号找到的是一级指针,再使用一个星号就会找到二级指针。
下面是二级指针的拆分说明:
字符指针
字符指针是存放字符的地址,如果存放字符串的地址就是首元素的地址。字符指针的类型是char*
指针数组
指针数组是数组还是指针,我们用语文的角度来分析,好孩子这里的主语是孩子,那么指针数组的主语就是数组,顾名思义就是存放指针的数组。
这是指针数组的模样:
int* p[10]
如何解读这个代码:
首先p 和[ ]结合,10说明有是个元素,每个元素的类型是int*.
指针数组存放的是数组的地址,那么数组的地址又是怎么表示的?且看下面讲解:
数组名的本质
一般情况下,数组名表示的就是首元素的地址,例如int arr[10] = {0};这里&arr[0]和arr 是一样的,我们可以验证一下:
但是也有特殊情况,有两种:
sizeof(arr) //这里的arr表示取出整个数组,sizeof 对整个数组进行 arr 的大小计算,arr在这里就不是首元素的地址。
&arr //这里的arr表示取出整个数组,拿到的是整个数组的地址,但是表示的时候还是使用首元素的地址。
那各位可能就有疑惑了,&arr和arr又有什么区别呢?
大家先来看看下面的代码:
这里的加1操作之后得到的地址又是不一样的,因为arr表示首元素的地址,因此加一操作后就是跳过一个元素,&arr取出的是整个数组,加一操作后跳过的是整个数组,所以它们在进行运算的时候会有所不同。
一维数组传参的本质
在没有使用指针的时候,一维数组在进行传参时,我们都会去使用数组来接收:
void test(int arr[])
{
...
}
int main()
{
int arr[10]={0};
test(arr); //arr本质就是数组首元素的地址
return 0;
}
在学习指针后,我们就可以使用指针来接受数组:
void test(int* parr)
{
...
}
int main()
{
int arr[10]={0};
test(arr);
return 0;
}
有了指针之后,我们也可以通过指针来访问数组:
这里我特意圈出来两个东西是 想说明:parr[i] 和 *(parr+i) 这两个是没有区别的,[ ] 和 * 都能起到解引用的作用。
既然大家对数组名已经有所了解了,那么我们也可以使用指针数组来模拟二维数组:
解释一下代码,每个数组名变成了指针数组的元素,第一个[ ]是表示第几个元素,第二个方括号是这个元素中的第几个数字。
当然也可以这样来解引用:
*( *( p + i ) + j)
这里有一个小练习:
#include <stdio.h>
int main()
{
char str1[] = "hello world!";
char str2[] = "hello world!";
const char* str3 = "hello world!";
const char* str4 = "hello world!";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
//说明运行结果是什么?
我们先上答案:
解析:
在 str1 和 str2 中,“hello world!" 是这两个不同的数组存放的字符串的,因此这一个字符串被拷贝了两份分别放在str1 和 str2 这两个数组里面,即str1 和 str2 所保留的地址是不一样的;在str3 和 str4 这里时,"hello world!“就是作为一个常量,在计算机里相同的常量只会留下一份,这样也可以节省空间,所以str3 和 str4 他们所指向的都是同一个“hello world!”,这样的话,str3 和 str4 他们所保存的地址是一样的。
数组指针
什么是数组指针,到底是数组,还是指针,我们来类比一下:整型指针是存放整型地址的指针,浮点型指针是存放浮点型的指针,字符指针是存放字符地址的指针…那么数组指针就是存放数组地址的指针,是指向数组的指针变量。
简单说明一下数组指针的样子:
int (*p)[10]
这里为什么要用()将* 和 p 括起来,因为 [ ] 的优先级要高于 * 的,所以要先保证 * 和 p 相结合。
我们来解读一下上面代码的意义:
我们去掉变量名,剩下的int (*) [10] 就是指针的类型。
二维数组的传参本质
二维数组的数组名也是跟一维数组的数组一样,都是表示首元素的地址,那对于二位数组来说,首元素是什么呢?二维数组的首元素是一行的地址。
大家来看一下应用吧:
函数指针
函数指针顾名思义就是指向函数的指针,存放的是函数的地址。
int (*p) (int,int) = Add;
如何解读:
首先我们先来看一下函数名和取地址函数有什么不同:
结果显而易见,&Add和Add没有任何区别。也就是说函数名其实表示的是这个函数的地址。
如何使用,其实很简单,就是更正常的函数传参一样,大家来看看代码吧:
这里解不解引用都没问题的,因为上面我们就知道函数名可以代表函数的地址。
函数指针数组
函数指针数组是数组,存放的是函数指针。
int ( *p[4] )(int , int) = {Add, Sub, Mul, Del};
如何解读这条代码:
那么这到底有什么作用呢?大家来看看下面的转移表:
转移表
利用函数指针数组实现计算器功能。
#include <stdio.h>
void menu()
{
printf("****************************************\n");
printf("***** 1.加法 2.减法 *****\n");
printf("***** 3.乘法 4.除法 *****\n");
printf("***** 0.退出 **********\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 Del(int x, int y)
{
return x / y;
}
void Tran(int n)
{
printf("请输入两个操作数:");
int x, y;
scanf("%d %d", &x, &y);
int (*pf[5])(int, int) = { 0,Add,Sub,Mul,Del };
int ret = pf[n](x, y);
printf("%d\n", ret);
}
利用函数指针数组就是我们的代码变得简洁,如果一个一个去输入的话,代码会显得过于臃肿,在以后设计代码程序时,我们也可以利用一些巧妙的方法来去使我们的代码更加轻便。