当前课程知识点:高级语言程序设计 > 指针 > 6.7 指针与二维数组 > 指针与二维数组
那么数组名是指针
我们看一看
二维数组名那又是一个什么样的指针呢
一维数组名是指针
二维数组名是一个什么样的指针呢
要把这个理解清楚
我们必须通过和一维数组的比较
这样通过比较
我们容易来认识清楚这一点
我们看一看
如果定义的是一个二维数组是这样的
int a[3] [4]
也就是一个三行四列的二维数组
数组名是a
数组元素的类型是int
那么它的内存里面这个存取
对应的矩阵是这样的
它的内存的程序是这样
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
它对应的矩阵
那么在内存里面存取的时候
我们可以用一个a[0]
那大家注意观察
这个第一行
第一行
这些名称里面有共同的部分a[0]
第二行这些名称里面有一个共同的部分a[1]
第三行这些名称里面有共同的部分a[2]
实际上这个a[0] a[1] a[2]是有意义的
它们是一个指针
今后我们就知道它是称为二维数组
对应的这个矩阵里面的每一行的指针
我们叫行指针
第一行的行指针就是a[0]
第二行的行指针就是a[1]
第三行的行指针就是a[2]
那么a是整个二维数组的首地址
那么我们也可以看出
它是指向a[0] a[1] a[2]这个指针
那么我们把a[0] a[1] a[2]理解称为指针的话
那这个就可以看成是一个指针的数组
是一个一维的指针数组
有三个元素
每一个元素
就是我们这对应的这个一行的行指针
那么a[1]实际上
就相当于是一个指向一个指针数组的指针
指向一个一维指针数组的指针
在C语言中
我们把二维数组中每一行
实际上是当做一个一维数组来处理的
也就是说二维数组
可以理解成为元素是一维数组的一维数组
那a就是指向了我们第一行
因为a在这里相当于a加0
相当于a[0]
所以a是这样
a+1那就指向第二行
a+2就指向第三行
那么对应的存储映射是我们看一看
a就是这个二维数组
这一块连续存储区的首地址
那a+1
根据前面的推理
它实际上就是a[1]的值
那么它就是我们第二行的行指针
它指向第二行的第一个元素a[1][0]
依次类推
a+2就指向第三行的第一个元素
也就是a[2][0]
那么在真正我们经过C语言的翻译编译的时候
是怎么保证a+1恰好移动一行的呢
那么如果我们是把数组名a
二维数组名a[i]
理解成为指针的指针是不行的
没有办法保证的
a+1恰好移动一行
因为根据前面我们所学的指针的运算
a+1是指向同类型的
下一个同类型的
那要a
如果是指向一个int类型的
这个指针的指针的
那a+1那是不行的
如果a是int类型的指针的指针
那a+1就不可能恰好移动一行
那你必须是一个指向一维数组
什么样的一维数组
就恰好是你这一行有几个元素
每一个元素是什么类型
是这样的一个
所以在这里我们就得出来
二维数组a是一个指向一维数组的指针
我们注意这里的元素的个数
必须是我们定义的二维数组的列数
这样我们把二维数组名的是一个什么样的指针
理解清楚了之后
下面我们就用指针来访问
二维数组就好办了
我们就会有针对性的
第一个方法
我们用指针数组加指针的指针来访问二维数组
我们先看这个程序
程序是这样的
#include
#include
#include
int main()
在这里我们定义一个二维数组a三行四列的
数组元素的类型是int的类型
我们给它初始化
第一行分别初始化为1 2 3 4
第二行是5 6 7 8
第三行9 10 11 12
而接着定义int的类型的变量i j
那么我们看一看
我们定义一个指针数组和指针的指针
一个指针数组是P
int *p[3]
p优先和中括号结合
所以p是一个数组
int*是数组中元素的类型
所以p是一个指针数组
逗号 **q
q就是一个指向int类型的指针的指针
我们可以把p的值赋值给q
因为p在这里是一个数组
这个数组已经刚才已经定义了
那么经过系统分配
这个p的值就已经有了
我们把p的值赋值给q
这是允许的
因为p是指向的
这个数组p里面的第一个元素p[0]
它是p[0]的地址
那么它是一个指针
这是一个指针
p[0]是一个指针
p[0]的地址赋值给q
那就是指针的指针类型也一致
for(i=0;i<3;i++)
那么花括号里面我们就可以把a[i]的值送给P[i]
这里由于a是二维数组
a[i]实际上就是行指针
a[i]的值实际上就是a+a[i]
然后在指针运算
那么数组p里面三个元素
分别指向了二维数组a里面的第一行
第二行
第三行
p[0]指向数字a的第一行
p[1]指向数组a的第二行
那么p[2]指向数组a的第三行
for(j=0;j<4;j++)
那么我们就可以用q[i][j]来代替a[i][j]
为什么可以用q[i][j]来代替a[i][j]
刚才我们再来看看上面的代码
我们是把p赋值给了q的
根据我们前面所学的推导的知识
那q[i]就相当于p[i]
不看这个p[j]
q[i]就相当于是p[i]
p[i]就相当于是a[i]
那q[i][j]那当然就相当于a[i][j]
根据我们的推导
因此我们就利用指针的指针q
来实现了对这个二维数组的访问
这里面每一次赋值的时候
大家主要要注意地址的类型匹不匹配
我们可以把这个#include
在Visio C++里面
我们可以通过puts(typeid(变量名).name());输出来
这个数据的类型
来判断检查一下刚才的类型是不是一致
地址的类型是否不一致
如果地址的类型不一致
也就是指针的类型不一致
那么是不能赋值的
那么赋值的也不会翻译
这是我们的方法1
用指针数组加指针的指针来访问
在指针的指针的时候
我们看一看
刚才用指针数组叫指针的指针的方法来访问了
它实际上是怎样实现的
我们看看这个示意图
这边是二维数组a
它对应的矩阵
那么p[0] p[1] p[2]
当我们把a[i]的值赋给p[i]的时候
那么p[0]就指向了二维数组a的第一行
那p[1]就指向了二维数种a的第二行
p[2]指向了二维数组a的第三行
由于p是指我们是把p赋值给了q
q就是指向这个一维数组
所以我们这个q[i]就相当于p[i]
q[i][j]那当然就相当于a[i][j]
因此我们这个运行结果
大家可以看看
输出来的结果
就和我们用这个数组下标的方法
输出的结果是一样的
1 2 3 4 5 6 7 8 9 10 11 12
恰好是这个二维数组
好这是指针数组加指针的指针的方法
访问这个二维数组
把刚才的这个访问过程我们做一个小结
我们看一看它是怎么来做到访问二维数组的
二维数组a
那么它对应的矩阵是这个样子
我们前面讲了a[0]是第一行的指针
a[1]是第二行的指针
a[2]就是第三行的指针
当我们把a[i]赋值给p[i]的时候
那么p[0]就指向了第一行
p[1]就只成了第二行
p[2]就指向了第三行
也就是p[i]就相当于a[i]
由于q是指向这个一维数组p的指针
q它是指针的指针
但是我们是把p赋值给了q
把p复制q
因为P的值
p是这个一维数组的数组
那么p的值实际上它就是放的
就是指向就是p[0]
因为p指向的是p[0]
所以p就是p中
那q就相当于p[0]
那q[i]就相当于p[i]
所以q[i][j]就相当于a[i][j]
那么过程是这样
q[i]那么等价于跟我们前面学习q+i*
做一次指针运算
那么由于我们把P的值赋给q了
那q+i作指针运算
就相当于p+i作指针运算
那p+i作指针运算
当然它等价于p[i]
前面我们学了p[i]就是p+i组成的
由于我们是把a[i]赋值给p[i]
所以p[i]那就相当于a[i]
那q[i][j]当然就等于a[i][j]
这个推导是这样的
那么这是用指针的数组
加指针的指针来访问二维数组
把刚才的过程我们做个小结
注意在定义运算符的时候
在定义的时候
运算符的结合与指针运算过程中这个类型的变化
运算符的一个作用
在上面数据定义里面
int *p[3]
由于中括号的优先级高于*
*是个单目运算的
中括号和圆括号是同一个优先级
当然高于*
所以p优先于中括号
那p就是一个数组
因为中括号是数组的标志
余下的这个int*就是数组p里面的元素的类型
所以p就是一个长度为3
也就是有三个元素
每一个元素的类型
是int*的这样的一个数组
所以它就是指针数组
这是一个
那么今后我们要判断一个标识符是数组
还是指针
还是函数
我们首先要看定义的时候
标识符是优先和*
还是和中括号
还是先和圆括号结合
如果先和*结合的
那是指针
先和中括号结合就是数组
先和圆括号结合的是函数
那么用运算符的优先级结合方向
我们来判断得先和谁结合
上面这个例子里面q是int*类型
也就是指针的指针变量
q+i仍然是int**的类型
q+i在组指向的对象
也就是q[i]
这个虽然还是指针
但是它指针的类型变成了int*
从指针的指针变成了一个int类型的指针
由于p是数组
它本来是指向一个有三个元素的
每一个元素是指针的
这样的数组
那么由于p是这个数组的首地址
也就是p[0]
它的地址
所以p看这里
当它赋值给q的时候
它就转化成了一个int**的指针
p是p[0]的地址
p[0]本来是一个int*的指针
那么p赋值给q这个类型是匹配的
C语言认为
p[i]的类型是int*
那么p[i][j]的类型就是int
p[i]是int类型的指针
那p[i][j]在做的一次指向的运算
指向对象的运算
也就是一个指针运算
就是int类型
访问的就是a[i][j]
这是我们用的指针数组
加指针的指针的方法访问二维数组
下面我们看看另外一种方法
方法2
用数组指针来访问二维数组
那么数组的指针如何来访问二维数组呢
我们来看看这个例子
程序是这样的
#include
#include
#include
int main()
花括号
a[3][4]
那么和刚才方法1里面是同一个二维数组
那么三行四列
第一行的值是1 2 3 4
第二行的只是5 6 7 8
第三行的只是9 10 11 12
下面我们看一看它怎么用数组的指针来访问的
int i,j
int (*p)[4]
在这里注意到p和*我们加了圆括号
那么p优先和*结合
所以p首先是一个指针
剩下来的int [4]
那就是P指向的对象的类型
因此P是一个指向对象
是一维数组的指针
我们把这样的点简称为数组指针
由于p的类型和a的类型一致
在前面我们讲的时候
我们讲到a
二维数组a的理解的时候
a就是一个int[4]
这个指向这样的一个一维数组的指针
所以p和a是同一个类型
那么我们就可以把a赋值给p
无非p是指针变量
a是指针常量
都是指向一维数组的指针
因此我们可以这样来访问了二维数组元素
a[i][j]
for(i=0j;i<3;i++)
for(j=0;j<4;j++)
调用格式输出函数printf( "%d\t"p[i][j]);printf("\n")
那p[i][j]访问了a[i][j]
因为p和a
无非一个是指针变量
一个是指针常量
p和a是同一个类型
能用a[i][j]输出二维数组中的元素
那当然就可以用p[i][j]来输出了
那么运行结果我们可以看到
恰好就实现了输出的二维数组
1 2 3 4 5 6 7 8 9 10 11 12
那么在这里
大家可以看一看
这个对这个p和a来访问
p和a的这个效果里面
p[i][j]就相当于是a[i][j]
那么大家可以在加深的来理解一下
因为刚才这个意思在这里已经指出来了
p优先与*结合
所以p是一个指针
那么它是一个什么样的指针呢
指向一个一维数组的指针
这个一维数组元素有四个
每一个元素是int类型
它和a是同一个类型
因此p+1就相当于a+1
p[i]就等效于a[i]
p[i][j]接呢就等下a[i][j]
我们把这个用数组的指针的方法
访问二维数组讲完了
谢谢大家
-1.1 计算机程序和计算机语言
-1.2 C程序的构成
--C程序的构成
-1.3 C语言编辑、编译、运行(VC++2010学习版)
-练习题
-2.1 变量的定义和使用
--变量的定义和使用
-2.2 数据类型
--数据类型简介
--整型数据
--实型数据
-2.3 格式输出和格式输入
-2.4 运算符和表达式
--运算符和表达式
-2.5 结构化程序设计入门
-练习题
-3.1 为什么要用选择结构解决问题
-3.2 如何正确表示选择结构中的条件
-3.3 用if语句及if语句嵌套实现选择结构
-3.4 用switch语句实现选择结构
-练习
-4.1 循环的引入
-4.1 循环的引入--作业
-4.2 用while语句实现循环
-4.3 用do-while语句实现循环
-4.4 用for语句实现循环
-- 用for语句实现循环
-4.4 用for语句实现循环--作业
-4.5 何时需要用循环的嵌套
-4.6 如何提前终止循环和提前结束本次循环
-习题--作业
-5.1 数组类型的引入
--数组类型的引入
-5.2 一维数组的定义和初始化
-5.3 一维数组的使用
--一维数组的使用
-5.4 二维数组的定义和初始化
-5.5 二维数组的使用
--二维数组的使用
-习题--作业
-6.1 什么是指针
--什么是指针
-6.2 指针类型及相关概念
-6.3 指针变量的定义、赋值与使用
-6.4 指针的运算
--指针的运算
-6.5 指针与一维数组
--指针与一维数组
-6.6 指针的指针
--指针的指针
-6.7 指针与二维数组
--指针与二维数组
-习题--作业
-7.1 为什么要自定义函数
-7.2 函数的定义
--函数的定义
-7.3 函数的调用
--函数的调用
-7.4 函数参数的传递
--函数参数的传递
-7.5 函数的嵌套调用和递归调用
-7.6 变量的作用域和存储类型
-习题--作业
-8.1 字符串的存储及输入输出
-8.2 系统字符串处理函数
-8.3 自定义字符串处理函数
-习题--作业
-9.1 结构体类型的定义
--结构体类型的定义
-9.2 结构体变量及指针变量的定义及使用
-9.3 结构体变量和指针变量作函数的参数
-9.4 结构体数组的定义和使用
-9.5 共用体类型和枚举类型
--共用体和枚举类型
-习题--作业
-动态内存分配--习题
-习题--作业