【C语言】数组与指针的关联(进阶)

我们已经对数组和指针有了基本的了解,接下我们深入了解一下数组和指针的关联。

一、数组名的理解

我们知道,对于一个内置数据类型的变量,变量名就是这个变量的值。例如:

那么,数组的数组名也表示数组中的值吗?但是数组中那么多值,表示那个呢?

答案肯定不是数组中的任何数值。

其实数组名表示的是数组的首元素地址!!!

我们通过一个代码来验证一下:

很明显发现,这两个的地址的值是相同的。验证了那句话:数组名是数组首元素的地址。

但是,在任何时候数组名都表示数组首元素的地址吗?

我们来看下面的代码(以32位的地址为例,此时指针占4个字节):

我们发现arr数组有10个元素,每个元素都是int类型,刚好占有40个字节,所以这时arr表示整个数组。

这也是第一种特例:当sizeof()括号中只有数组名时,这个数组名表示整个数组。

接下来再看一个代码:

这是第二种特例:对数组名取地址操作,这个数组名表示整个数组。

对于数组名,只有这两个特例,在其他情况中数组名都是表示数组首元素的地址。

二、指针访问数组

在清楚数组名表示数组首元素的地址后,我们就可以使用指针接受数组首元素的地址,然后通过指针去访问数组中每个元素。

由于数组名本身就是指针,所以我们还可以这样写:

从这里我们可以看到,arr与p是等价的。我们不由得想到:我们之前访问数组是通过arr[i]来访问,那么我们能不能将这里的arr换成p呢?来看下面代码:

很明显可以这样做,所以p[i] == *(p + i)。

实际上,对于arr[i],在编译器处理的时候,会将这个表达式转换成*(arr + i),求出该元素的地址后再进行解引用。

三、一维数组传参本质

我们在对数组名有了一个了解后,我们来探讨一下一位数组传参的本质。

首先,我们思考一个问题:

我们来测试一下:

不能,原因是什么呢?

还记得在上面讲的数组名除了哪两个特例外都表示数组首元素的地址吗?调用函数中使用数组名并不属于那两个特例,所以这里传的是数组首元素的地址,形参ar接受这个地址。因此,形参ar本质上是一个指针,sizeof(ar)计算的是指针的大小,我们不能通过函数得到数组中元素的个素。

我们可以验证一下形参ar是不是指针:

结论:一位数组传参时,形参可以写成数组的形式,也可以写成指针的形式,但本质都是指针。

四、二级指针

指针变量同样也是变量,是变量就会有地址,那么指针变量的地址存放在哪里呢?二级指针

所以,pp一次解引用(*pp)是p中的值,也就是a的地址,pp解引用两次才是a中的值(0)。

五、指针数组和数组指针

对于这两个概念,大家十分容易弄混,接下来我帮助大家辨别这两个概念。

1. 指针数组

首先,我们思考一下:指针数组究竟是指针呢,还是数组呢?

在回答这个问题之前,我们想象我们之前的数组:整型数组 int arr[ n ] ; 存放整型变量的数组

字符数组 char arr[ n ];存放字符型变量的数组……

那么指针数组呢?当然是存放指针变量的数组了。所以,指针数组依然是数组。

那么我们怎么创建一个指针数组呢,来看下面图片:

1️⃣指针数组模拟二维数组

我们在数组名中知道:可以使用指针接受数组名来访问数组,将指针集合为一个数组,不就是将几个一位数组集合为一个数组,就是二维数组。

我们虽然也能通过arrp[i][j]访问到元素,但是这只是模拟二维数组,与二维数组有一个本质的区别:二维数组每一行是连续存放的,但指针数组不是连续存放的。

2. 数组指针

既然指针数组是数组,数组指针肯定不是数组了,而是指针。我们举个熟悉的指针来导入它。

例如:整型指针,int* pi; pi中存放一个整型变量的地址,可以指向一个整型变量

    字符指针,char* pc; pc中存放一个字符型变量的地址,可以指向一个字符型变量

那么对于数组指针,就是存放一个对应类型数组的地址,可以指向一个对应类型数组。

我们如果要存放(int arr[5];) arr这个数组,我们应该怎么创建对应的数组指针呢?

我们可以看一下p,&arr,arr的数据类型能更加清楚:

我们发现,数组指针与指针数组的创建很像,就是差了一个(),那么这个括号怎么决定了是指针数组还是数组指针呢?这就与操作符的优先级有关了。

[]的优先级高于*,所以当我们写成:int* p[n];p先于 [] 结合,这就表示了p本质是一个数组,数组中的元素类型是前面的 int*;但我们写成:int (*p)[n];由于()的优先级最高,p先于* 结合,表示p是一个指针变量,指向的对象类型是一个数组,这个数组中有n个元素

在对于数组指针有个了解后,我们来分析一下数组指针的类型:

我们在上面说到:二级指针不能等价于二维数组,指针数组又只能去模拟二维数组,那么二维数组的地址应该村放到哪里呢?答案只剩下了数组指针,所以二维数组地址存放到数组指针中。

我们可以通过一个代码来证明:

结论:我们通过指针访问一维数组,访问二维数组则是通过数组指针。我们在下面介绍原因。

六、二维数组传参本质

 通过上面我们了解了数组指针与二维数组之间的关系,这时来探讨二维数组传参本质。

我们之前写二维数组传参的的写法:

这里的实参是二维数组,形参也是二维数组的形式,跟我们最开始一维数组传参一样,形参和实参都是一维数组,但是一维数组形参可以写成指针,那么二维数组的形参有没有别的写法呢?

我们再次理解一下二维数组:二维数组可以看成每个元素都是一维数组的数组,那么二维数组的每一行就是一个一维数组,如下图:

依据数组名是数组首元素的地址这个规律arr表示这个二维数组第一个元素的地址,也就是一个一维数组的地址(&arr[0])。所以,二维数组传参本质上也是传了一个地址这个地址是第一个一维数组的地址,而接受一个数组的地址我们使用的是数组指针,所以二维数组形参还可以这么写:

结论:二维数组传参,形参可以写成二维数组的形式,也可以写成数组指针的形式。

七、字符指针变量

我们知道这种指针:char* --- 字符指针

我们之前这么使用:

char ch = 'a';
char* pc = &ch;

但这种指针还有一种使用:

但是我们知道,指针变量只能存放地址,怎么能存放一个字符串的值呢?这是将字符串中第一个字符的地址存放到了指针p中。

我们通过一道《剑指offer》中的题来更深入的理解字符串:

可以思考一下会打印句1还是句2,打印句3还是句4。

我们看一下答案:

打印了句2和句3.

我们分析一下为什么打印这两句:

首先分析前两句,我们是先创建的数组,再对数组中的元素初始化。那么我们创建的这两个数组地址在创建时就不会一样,我们只是对这两个数组的初始化一样,但地址是在创建数组时就确定了,跟我们初始化了什么值无关,所以str1 != str2;

然后分析后两句,我们是创建的指针,然后将字符串的首地址赋值给指针一个字符串在被创建后再被使用,不会再开辟空间创造重复的字符串,会直接使用先前已经创建好的字符串那么我用两个指针接受这个字符串,只能说明这两个指针指向同一个地址,所以str3 == str4;就像int a = 3;int b = 3;if(a==b)答案肯定会执行if中的语句。

  • 47
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值