五. 指针和数组
我们上篇文章讲解了指针的相关知识,但是并没有明确地把指针和数组放在一起来讲解,那么今天我们主要来讲讲指针和数组之间的关系
首先,指针就是指针,它是一个变量,不是数组,是专门用来存放地址的。
其次,数组就是数组,不是指针。它是一块连续的空间,用来存放一个或者多个相同数据类型的元素。
但有同学可能会问了,他俩有什么关系?
确实,根据定义来看,两者确实没有什么关系,一个是指针变量,一个是数组,但是别忘了,数组有一个特殊的点,那就是(在绝大多数情况下)数组名就是数组首元素的地址,于是通过这个,将数组和指针连续在了一起,所以,我们也可以通过指针变量来遍历数组。
以一个整形数组为例,其中有十个元素,我们分别赋值为 1 - 10。
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 打印每个元素的地址
for (int i = 0; i < 10; i++)
{
printf("%p\n", &arr[i]);
}
int* p = arr; // 首元素地址
// ***通过四种方式来遍历数组中的元素***
for (int i = 0; i < 10; i++)
{
//printf("%d\n", *p++);
//printf("%d\n", *(p + i));
//printf("%d\n", arr[i]);
//printf("%d\n", i[arr]);
}
return 0;
}
怎么样,通过今天的讲解,相信大家对数组,数组名,指针,以及他们之间的关系有了更深一步的了解了吧。
6. 二级指针
学完了(一级)指针,以及指针和数组之间的关系后,我们将学习一个新的变量名:二级指针。
在此之前,我们先回顾一下我们之前学习的指针变量。
首先,我们常说的指针,也就是指针变量,是一个变量,是用来存储地址的,至于存储什么变量类型的数据,跟指针变量的类型有关。
那么我们都知道,只要是变量,就会有地址,因为变量是在内存中开辟空间的。
那么,我们通过什么方式来获取一级指针变量的地址呢?
于是,我们引出了二级指针变量,顾名思义,跟一级指针变量相同,他也是一个变量,只不过它内部存储的是一级指针变量的地址,我们举一个例子来说明
int a = 10;
int* p = &a; // 一级指针
int** pp = &p; // 二级指针
我们定义了一个整数 a = 10,定义了一个一级指针变量指向变量 a,这个时候 p 就是一个一级指针,用来存储整型变量的地址,数据类型为int*,其中*的含义是我是一个指针,int的含义是我指向的变量是 int 类型的。
那么,二级指针同理,pp 就是一个简单的二级指针,他的数据类型为 int** 第二个 * 代表我是一个指针,前面的 int* 代表 我指向的变量是 int* 类型的,怎么样,将数据类型拆开来看的话,是不是就更容易理解了呢?
下面,我再举一个例子,希望能加深你对二级指针的理解。
int main()
{
char arr1[] = "abcedf";
char arr2[] = "hello bit";
char arr3[] = "cuihua";
char* parr[] = { arr1, arr2, arr3 }; // 指针数组,存放指针的数组、
char** p = parr; // parr为指针数组的首元素的地址
}
我们定义了三个字符串,分别为arr1,arr2,arr3,当然了,你也可以把他们当作是 三个字符型数组,所以 arr1,arr2,arr3分别是三个数组首元素的地址,我们把它们拿出来,放进parr中,所以毫无疑问,parr是一个指针数组(指针数组的定义我们后面会提到,说白了指针数组就是存放一大堆指针的数组)。那么,同理parr数组的数组名就是parr数组首元素的地址,这时候,我们要想用一个变量来接受parr,就需要用一个二级指针变量来存放一级指针变量的地址,所以 p 是一个二级指针。
那么,我们如何通过二级指针,来找到一级指针,从而找到一级指针指向的变量呢?
int main()
{
int a = 10;
int* p = &a; // p 是一级指针变量,指针变量也是变量,是变量就有地址,因为变量是在内存中开辟空间的
int** pp = &p; // pp 是二级指针变量,就是用来存放一级指针变量的地址
// 二级指针中的第二个*代表pp是指针变量 前面的 int* 代表 pp指向的是int*类型
**pp = 100; // 修改 a 的值
printf("%d", a);
return 0;
}
我们定义了一个 int 类型的数据,并且有两个指针,分别为一级指针和二级指针,我们如何通过二级指针来修改 a 的值呢?
那么这就涉及到了解引用的知识,我们可以通过解引用的方式来找到原变量,从而对他进行赋值。
首先 我们通过二级指针来找一级指针,通过 *pp 的方式来找到一级指针,然后再通过一级指针来找到原变量,*(*pp),当然括号可以省略不写,于是我们得到结论:若想通过指针解引用的方式来找到原变量,是几级指针就要在前面加上几个*。
到这里,相信你对二级指针的理解已经足够深刻了,那么,让我们继续往下学习吧!
七. 指针数组
指针数组,指针数组,到底是指针还是数组?
相信初学者看到这里脑袋就有一点蒙了,于是就开始胡思乱想了。
其实,如果我们静下心来想一想我们之前学过的整形数组,字符数组,其实答案已经是显而易见了。
什么是整型数组? 答:存放一大堆整数的数组
什么是字符数组? 答:存放一大堆字符的数组
那么什么是指针数组?答:存放一大堆指针的数组
那么指针数组到底是指针还是数组?答:是数组
清楚了这个之后,其余的事情就很好理解了。
int main()
{
char arr1[] = "abcedf";
char arr2[] = "hello bit";
char arr3[] = "cuihua";
char* parr[] = { arr1, arr2, arr3 }; // 指针数组,存放指针的数组
for (int i = 0; i < 3; i++)
{
printf("%s\n", parr[i]);
}
char arr[] = "abcdef";
printf("%s", arr);
}
同上文的代码,parr就是一个指针数组,存放着三个字符数组的指针,我们可以通过数组指针中的元素找到对应的数组并将它们进行打印。
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[] = {arr1, arr2, arr3}; // 指针数组
//parr[0] = arr1; 第一个数组的首元素地址
//parr[1] = arr2; 第二个数组的首元素地址
//parr[2] = arr3; 第三个数组的首元素地址
return 0;
}
我们定义了三个整型数组,分别为arr1,arr2,arr3,然后定义了一个指针数组parr,那么我们如何通过parr来打印arr1,arr2,arr3中的全部内容呢?
其实很简单 parr[0] == arr1 = &arr1[0],parr[1] == arr2 = &arr2[0],parr[2] == arr3 = &arr3[0]
那既然我们知道了每个数组首元素的地址,不就能通过指针来依次访问数组中的每一个元素了吗?
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
//printf("%d ", parr[i][j]); 类似于二维数组
// 真实的二维数组是连续的,这个不一定
/*printf("%d ", *(parr[i] + j));*/
/*printf("%d ", j[i[parr]]);*/
}
printf("\n");
}
形式上看起来好像是对二维数组进行打印,其实不然,我们之前讲过,二维数组内存中的存放是连续的,而这里三个一维数组在内存中的存放未必是连续的,甚至大概率不是连续的,希望大家不要把指针数组和二维数组弄混,他们两个其实是毫无关系的存在。
好了,今天关于指针的分享就到这里了,我们通过两节课的时间,把指针初阶的内容大概讲解了一遍,如果大家还有什么困惑,可以在后台私信我,看到问题我会及时回复的!
谢谢各位的点赞和关注,那我们下期再见!