那接下来呢,我们进入今天一个主要的内容,
指向二维数组的指针。
那么在开始讲这部分之前呢,我首先说明一下。那么,指向二维数组的指针啊,
对同学们来说可能是一个难点。比方说在我们这个课上,
有些同学啊,可能践足于一些参考教材,往前学了一些。
他的进度比我们课程稍微快了一点。 当他看到这个部分的时候呢,他就产生了很多的问题。于是呢,就写邮件给我,来询问这些问题。
我很理解这个状况。首先呢,这部分啊稍微有一点点的麻烦。
而且呢,我也确实看到,在很多的教材上解释得不是很清楚。
啊,所以说呢,给同学们的理解造成了麻烦。 其实啊在这一部分很关键是一个理解的问题。
Ok ,那接下来啊,在介绍指向 二维数组的指针之前,我们首先啊,先再来谈一下一维数组的
地址的问题。Ok ,大家还记得这一页 PPT 吗?
哪,在这一页 PPT 上啊,我解释了一个问题。
如果你定义了一个指针变量 p2。
它呢,指向了一个普通的整型的变量 a。
啊,因为 a 是一个整型的变量,int 型的变量,所以说,
a 在内存空间中占了1,2,3,4 ,4 个字节。
这个时候呢,如果我们对指向 a 的指针变量 p2 进行
++ 操作的话, 那么,得到的结果并不是对 p2
的这个地址进行了 +1 的操作, 而是进行了地址 + 4
的一个操作。所以经过 ++ 以后呢,p2 移到了这里。
啊,也就是说,一下跨越了 4 个字节。这是我们曾经讲过的。
总结一下的话就是说,当我们对
一个指向整型的指针变量进行 ++ 操作的话,
那么指针变量的值一下子增加了 4。
啊,这是上次课我们曾经讲过的。我们还说,这个东西呢,它体现了
基类型的作用。啊,就因为定义了这样一个基类型,所以才会出现 这样的一个结果。Ok
,那么回顾完这次讲的知识点以后啊, 我们就来看一段程序。我们来看这个程序,读一下这个程序。
想一下,这个程序的输出啊,会是什么样子的?
在这个程序里头啊,首先呢,我们定义了一个数组,int a[4] =
1,3,5,7 数值。 那么当我定义完这样的一个数组的时候啊,那么肯定在内存里头
就开辟出一片存储空间,并且呢,把这个数组放进去。
比方说,啊内存的状况是这样的,4个数组元素摆在这里。 a[0], a[1], a[2],
a[3]。 这一边呢,都代表内存地址,啊这都是内存的地址。
当然,内存的地址是一个字节一个字节的来标定的。
啊,比方说这是一个字节,那么这就是由一个字节相对应的内存地址。 Ok
,那我们就来分析一下底下程序的运行过程。首先啊,我在这里打印数组名
a 的值。 毫无疑问,因为数组名是数组首元素的指针,也就是数组首元素的地址
也是数组首元素的起始地址。所以说在这儿毫无疑问会打印这个值
0028F7C4。 会打印这样一个值。这是毫无疑问的。Ok,
再往下,我们去打印 a+1 的值。 这个时候啊,可能有的同学就有不同的建议了。那
a+1 的值应该是多少呢?
到底是在 0028F7C4 的这个基础上 +1 得到
0028F7C5 呢,还是要打印一个其它的值呢
? 要分析清楚啊,也不难。我们只要搞清楚这个
a 是什么含义就可以了。 对不对?a 的含义我们再清楚不过了。
a 是数组的名字,也就是
指向数组首元素的那个指针。数组的首元素,第一个元素是什么类型的啊? 是
int 类型的元素。我们刚刚讲过,当一个指针变量指向一个
int 型的元素,而我们对这个指针变量进行 +1 的操作的时候,它会跨过
几个字节啊?会跨过 4 个字节。 也就是说,当对 a
进行 +1 的时候,它会直接跨过 4 个字节到哪儿啊?
到这里。 到这里。也就是说, a+1 的值会打印哪一个值啊?会打印这个值。
0028F7C8 ,一下跨过了 4 个字节。
啊,我相信呢,这个,大家也不难理解。Ok, 这是这两个。
继续往下看。接下来呢,我要打印的东西叫做 &a。
&a 。有的同学在这儿可能就产生疑问了。
a 呢,它是一个常量,它不是一个变量。
那么在 a 的前面再加一个 & 的取地址符号,这个怎么解释呢?
这个程序能正确运行吗 ?
啊,可能很多同学都会有这样的疑问。 Ok,
那其实关于这个问题呢,在上一节课被更新以后的
视频中,我已经给出来解释了。那么在这儿呢,我再解释一下。
这是上次课,我们去解释数组的名字相当于指向数组第一个
元素的指针的时候,我们用的一张 PPT 。那么在这个
PPT 上呢, 我列出来了 3 个从 C
语言的规范中截起来的文字的片段。
那么,在中间的这一段,关于数组类型的表达式,比方说数组名,
它的类型的解释中,明确的指出来了这样的一个问题。
就是说,如果这个数组名啊,except when it
is operand of 它,它和 & operator
。如果它不是这个 & 的操作符的一个操作数的时候,
也就是说,如果一个数组的名字
不出现在这个取地址符号的后面的时候,
那么,它的类型将被转换成 an expression with type pointer to type。
也就是说,它就会被转换成为指针类型。
而且这个指针类型是指向哪的呢?指向数组的第一个元素的。
那么这一段呢,就解释得非常的清楚了。就是说,一个数组名,
如果不出现在取地址符号后面的话,那么它呢,就相当于
指向数组首元素的指针。那么如果出现在这个取地址符号后面
会怎样呢?那么根据 C 语言的规范,
当一个数组名出现在 & 的符号以后的话,
它会返回一个指向这个数组的一个指针。
什么意思呢?根据 C 语言的规范,当你把一个有
E 的表达式写在 取地址符号后面的时候,比方说,&E,
那么,根据 C 语言的规范呢,整个这个式子
返回的将是指向这个 E 的一个指针。
那么在这个程序里头,出现在取地址符号后面的是数组的名字
a。 那么根据取地址符号的这个运算规则,在这个时候,整个这个 &a
返回的结果将是指向数组 a 的一个指针。也就是说,这个指针的类型
不再是指向数组的第一个元素的了,而是指向整个数组的
一个指针。Ok ,那么下面我们回到这个例子,再跟大家说明一下。
也就是说,原来呢我们只打印 a 的时候,那么这个程序呢,将会打印这个地址。
对吧?那么现在呢,当我们打印 &a 的时候,
它依然会打印这个地址。 但是,&a 跟 a
有什么不同呢? a 呢,是相当于指向数组首元素的那个指针。
而 &a 也就是取地址符号加上 a ,相当于什么呢?
相当于指向整个数组的一个指针。
啊,也就是说啊,这两个 a 啊,
虽然打印出的地址是同一个地址,
但是它们的管辖范围不同。 那么这个 a
呢,它的管辖范围只是数组的首元素。但是 &a
这个表达式所返回的那个指针,
它的管辖范围是哪呢?是整个数组。是整个数组。
虽然他们打印出的地址是一样的,但是它们的
管辖范围完全不同。啊也就是说,
如果在数组名字前面加上一个 & 的符号的话,
就相当于把这个数组名字的管辖范围
上升了一级,给它升官了。明白这个意思了吗? 啊,这就是
&a 的含义。好! 我们呢清理一下屏幕,接着往下看,
通过刚才我们这个介绍啊,大家都知道,
&a实际上是一个指向整个数组的一个指针。
那么在打印完&a以后,我再去打印 &a+1,请问它会打印哪个地址呢?
那根据我们前面讲过的,如果对一个指针进行+1的话,
它会跨过它的基类型所占用的全部内存单元。
对不对,如果对a进行+1,我们得到了这个地址,
得到了这个结果;如果对&a+1的时候,我们应该得到哪个结果啊?是不是从这
将跨越整个数组啊, 对不对,将会跨越这个数组a+1。所以说
&a+1会指向哪里?会打印哪个地址啊,会打印这个地址。
这是&a+1的这个涵义。 它会跨越整个数组。好,说到这呢,我们不妨先来看一下
这个程序的执行结果。这是这个程序的执行结果,我们来核实一下。
当我去打印数组名a的时候,
我得到的是0028F7C4,也就是这个地址,
这毫无疑问是正确的。当我去打印a+1的时候,我所得到的是,
0028F7C8,是这个地址,这个也是正确的。
当我再去打印&a的时候,我们说这个时候,
虽然它跟a的结果应该是一样的,但它的权限范围不同。
我们先来看打出来的结果是不是一样的,0028F7C4, 跟这个结果是一模一样的。
那我接下来再去打印&a+1,那么根据我们的分析它将打印这个地址。我们来看一眼, 是不是0028F7D4?没有错这是这个地址。