1.二级指针
我们已经知道了什么是一级指针了,一级指针就是存放变量地址的指针变量,那么同样是变量,指针变量也是有自己的地址,只不过一级指针接收的是变量的地址,那么按照一级指针的理解,二级指针就是用来接收一级指针变量的地址,下面就是二级指针。
当然我们也是可以通过二级指针解引用来获得变量a的值,相互转换就如以下可以知道。
*ppa=pa
*ppa==&a
* *ppa==*&a
*&a==a
2.指针数组
什么是指针数组呢?我们可以通过类比法可以知道,我们知道有整型数组(存放整型的数组)、字符数组(存放字符的数组)、浮点型数组(存放浮点型的数组),那么我们通过类比就可以得知:指针数组就是用于存放指针/地址的数组。
指针数组的每个元素都是用来存放地址(指针)的。例如下图,指针数组的每个元素都是地址,又可以指向一块区域。
3.指针数组模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);//parr[i][j]可以写成*(*(parr+i)+j),两者都是一样的
}
printf("\n");
}
return 0;
}
从运行结果可以看出输出的结果是一个二维数组。
4.字符指针变量
我们已经知道了整型指针变量了,是存放整形变量的地址。那自然而然的,字符指针变量就是存放字符变量的地址。
上图是我们常用的表示方法,但是还有一种使用方式如下:
其实并不是,这里的意思是把hello world这个字符串的首字符的地址放到了pstr中,也就是把h的地址放到了pstr中。
5.数组指针变量
我们在上面知道了指针数组是一个用于存放指针的数组,那么现在碰到的数组指针是什么呢?
我们刚刚知道了字符指针变量是用于存放字符变量的地址,那么通过类比可知,数组指针变量是用于存放数组的地址的指针变量。下面给大家看一下数组指针的形式
这里的p先与*结合,说明p是数组指针变量,然后指向的是一个大小为10的整形数组,所以p是一个指针,指向一个数组,叫数组指针。
这⾥要注意:[]的优先级要⾼于*号的,所以必须加上()来保证p先和*结合。
6.二维数组传参的本质
在前面我们已经知道了一维数组传参的本质,形式有两种,那么二维数组传参的本质也同样是有两种,跟一维数组传参是大同小异,话不多说,我们来通过代码的形式理解
#include<stdio.h>
void test(int a[][5], int x, int y)
{
for (int i = 0; i < x; i++)//控制行
{
for (int j = 0; j < y; j++)//控制列
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int a[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(a, 3, 5);
return 0;
}
运行结果如下:
上面是一种二维数组传参形式,还有一种那就是通过指针来接收,在前面我们已经知道,数组名是数组首元素的地址,那么我们传参过去的时候传的就是数组首元素的地址,我们知道二维数组其实就是一维数组的数组,那我们传过去的就是即就是第一行的地址,也就是一维数组的地址,但是每一行里边还有五个元素,那么我们就通过传过去的指针再指向这五个元素就可以了,如下图
#include<stdio.h>
void test(int (*p)[5], int x, int y)
{
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
printf("%d ", *(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int a[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(a, 3, 5);
return 0;
}
从中看出,p接收了二维数组中行的地址,然后p再指向每一行中的五个元素,这样就可以得到每一个元素
7.函数指针变量
根据数组指针以及字符指针类比,可以知道,函数指针变量就是用于存放函数地址的指针变量。
但是&函数名与函数名的地址有什么区别吗?下面这段代码可以给我们解答。
从中可以看出,无论是&函数名还是函数名的地址都是一样的,这就跟数组有点类似,但还是有所差别。下面我来总结一下这两种区别:
&数组名:取出的是整个数组的地址。
数组名:数组名是数组首元素的地址。
&函数名:得到的是函数的地址。
函数名:得到的也是函数的地址。
以下是函数指针变量的形式:
那么函数指针应该怎么使用呢?
上面的代码,我们可以看到在输出的时候不论pf是否解引用,输出的结果都是正确的。
8.typedef关键字
typedef是用来重命名的,可以将复杂的类型简单化。
例如,unsigned int写起来不方便,我想用uint来代替它,那么我们可以这么写:
typedef unsigned int uint;
这么写之后,后面的代码如果要用到unsigned int 的话都可以用uint来代替它。
但是如果是指针类型我们应该这么重新命名呢?其实跟上面差不多。
typedef int* ptr_t;
这个就可以同ptr_t来代替整型指针int*了。
但是在重新命名函数指针和数组指针的时候就会有点差别。
typedef int (*parr_t)[5];//这里把int (*)[5]重命名为parr_t;需要注意的是命名的名字要放在*的右边
9.总结
通过这篇博客学习知道了,1.二级指针就是用来存放一级指针变量自身的地址的,通过双重解引用就可以得到变量的值;2.指针数组是存放指针的数组,数组中的每一个元素都是地址;3.字符指针是用来存放字符变量的地址的变量,字符指针有两种使用方式;4.数组指针是存放数组的地址的变量;5.二维数组传参与一维数组传参是差不多的,二维数组其实就是一维数组里还有个数组,用指针接收时,接收的是二维数组中第一个元素的地址,也就是第一行的地址;6.函数指针变量中的函数名与&函数名都是代表的是函数的地址;7.typedef可以对复杂的名字进行重命名使得简单化。