指针进阶知识超详细讲解(C语言)(中)

前言
苦苦等待,终于迎来了我们的指针讲解第二期,如果有还没有看过上一期的小伙伴,那赶紧点击这个链接学习后再来看这篇博客吧。链接:指针基础知识超详细讲解(C 语言)(上)
如果对学习C语言有兴趣那千万不要忘记关注本专栏哦。O(∩_∩)O (也关注下博主吧!): C语言学习专栏

在这里插入图片描述


上一篇博客我们学习了基本的指针类型,指针的加减运算,以及野指针的成因和避免野指针的方法。今天这一篇博客我们将进一步学习指针。那么久让我们开始吧!

1,多级指针

首先让我们写先一个简单的指针变量。
int a = 10;
int* pa =&a;
这里的int* 代表pa的数据类型是整形指针。那么这个呢?
int* * ppa = pa;
我们知道pa存的地址会指向整形变量a,所以我们用int类型来定义pa,如果我们要吧pa的地址在储存在一个指针变量中,那么我们就应该用int * *来定义ppa,int *指得意思是ppa存的地址指向的变量的数据类型就是int *类型的,之后的 *的意思是指ppa是个指针。
那么这么定义ppa的意思就是ppa是一个储存 数据类型为int 的变量的地址 的指针。
这个就是二级指针。
那我们再定义一个指针。
int
* * * pppa = ppa;
与上面同理,ppa就是一个三级指针,两级以上的指针就被我们称为多级指针。
我们知道从pa中取出a我们要解引用 * pa=a
那么我们要从ppa中取出a就要这样解引用 * ( * ppa)= * pa = a
pppa同理。

2,一维数组传参的本质和数组名的含义

假如这里有一个函数,它的参数是一个数组,我们要如何传递这个参数呢?可以看看下面的示范。

#include<stdio.h>
viod test(int arr[])
{
    
    
;
}
int main()
{
    
    
int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}

我肯可以看到我们直接将数组名传到函数中了,这是个什么原理呢?接下来我来为大家介绍数组名的本质。
当arr单独存在时,它代表数组首元素的地址,我们将arr作为参数传过去实际上是把arr数组的首地址传了过去,那么这时候大家会疑惑?既然我传过去的是地址,为什么我要把参数设置为int arr[ ]呢?实际上int arr[ ]等价于int * arr也就是说,我们也可以把参数的格式写成int * arr。
但是也有特例的情况比如我们要打印数组大小的时候会写出这样一段代码。
printf(“%zd”,sizeof(arr));
此时arr也是单独存在的,但是这是他并不是数组首元素的地址,而是代表整个数组,除了这种情况以外,数组名单独出现都代表数组首元素的地址。
既然这样&arr代表着什么呢?我们不妨写下这么一段代码测试一下。

#include<stdio.h>
int main()
{
    
    
int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
printf("%p %p",arr,&arr);
return 0;
}

在这里插入图片描述

我们会发现两者的地址是一样的难道&arr代表首元素的地址么?那我们对各自地址+1在打印看看。

#include<stdio.h>
int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	printf("%p %p %p",arr, arr+1, &arr+1);
	return 0;
}

在这里插入图片描述
可以看出结果却不一样arr+1相较于arr多了4,刚好是一个整形的大小,而&arr+1比起arr缺多了整整40,正好是整个数组的大小。对&arr+1跳过了整个数组的大小。说明&arr是整个数组的地址。
既然arr单独出现时代表数组首元素的地址,那么我们对arr进行解引用,我们就会得到数组的首元素 。 * arr = 1
那个对arr+1解引用自然就是数组的第二个元素了 *arr+1 = 2
那么我们就会发现 * arr 与 arr[0]与 * arr+0是等价的, * arr+1与arr[1]是等价的。当我们想通过循环打印整个数组时我们就可以这么写。

for(int i = 0;i <= 9 ;i++)
{
    
    
printf("%d",*arr+i);
}

3,指针数组

指针数组,顾名思义是个数组,整形数组里面装的是整形,字符数组里装的是字符,那么指针数组里装的自然就是指针。最基本的整形指针数组是这样定义的(数组里的元素是整形指针)。
int* arr[10] ;(arr是数组名,数组中有十个元素)
我们可以在其中存入整形指针。

4,数组指针

数字指针,显然它是个指针,他储存的是一个数组的地址,我们先来定义一个整形数组指针(存的是整形数组的地址)来做个示范吧。
int arr[2]={1,2};
int ( * parr)[2] = &arr;([ ]的优先级高于*,所以要把*parr用括号()括起来表明parr是指针,[2]指该数组指针指向的数组有两个元素)
那么数组指针加减运算会有什么结果呢?

int main()
{
    
    
	int arr[4] = {
    
     1,2,3,4 };
	int(*parr)[4] = &arr;
	printf("%p %p", parr, parr + 1);
	return 0;
}

在这里插入图片描述

我们会发现刚好+了16,正好是arr数组的大小,可见对parr+1会跳过整个数组的大小。
如果对parr解引用,那么我们就会得到数组arr的地址,即arr首元素的地址。

5,二维数组传参的本质

首先我们可以把二维数组理解为一个一维数组,而这个一维数组里面每一个元素都是一个一维数组,即二维数组的每一行都是他的一个元素。对arr解引用会得到其第一个元素的地址,即第一行的一维数组。对arr+1解引用则会得到第二行的一维数组的地址。
在这里插入图片描述

我们将二维数组传参是可以有着两种写法。

void test(int(*parr)[2])
void test (int arr[][2])//行数可以不写但是列数必须写

这两种写法是完全等价的,因为* parr = arr [ 0 ]= * arr+0 **parr = arr[0][0]=**arr.
对二维数组传参其实传的是二维数组第一行的地址。
*((*parr+1)+1)就等价于 arr[1][1]。

6,尾声

经过这篇博客,想必大家对数组和指针的了解有了进一步的加深,认为博主写的不错的千万不要忘记关注博主等待指针讲解(下),希望大家都能够学好指针(指针很重要),想着自己的目标再进一步!
最终期链接,冲冲冲!

猜你喜欢

转载自blog.csdn.net/Tokai___Teio/article/details/134740895