《C语言程序设计》指针篇<二>
通过指针引用多维数组
如何理解二维数组元素的地址?
要知道,这本书用了整整两页的内容来讲解这方面的知识,从这里足以看出来理解通过指针来引用二维数组是一件比较麻烦的事情,但是我认为理解并不难。
- 什么是二维数组?
举个例子:int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};a是二维数组名,可以看成一个大数组“包着”三个小数组:
大数组(管理小数组的数组)a包含三个元素(小数组):a[0],a[1],a[2]。小数组a[0]包括四个元素:1,3,5,7。小数组a[1]包括四个元素:9,11,13,15。小数组a[2]包括四个元素:17,19,21,23。
二维数组:“小数组的大数组”
一.数组名a和&a[i]
回到我们指针的内容,之前我们理解过,数组名代表该数组首元素的地址(
&a[0]),那么这里的二维数组名也可以这样子理解为是&a[0],但是我们上面提到,a[0]也是一个数组,那么&a[0]代表的是什么意思呢?事实上,它是一种地址的计算方法,得到的答案是小数组a[0]首元素的地址。从我上面所述的二维数组是“小数组的大数组”观点来看,数组名a与二维数组首元素的地址(&a[0])等价,但是由于这里首元素a[0]是一个小数组,我们可以看作a指向大数组的第一个元素:小数组a[0] 的第一个元素a[0][0]。也就是说,数组名a的值是
&a[0][0]。类比可得:
&a[i]就是小数组a[i]首元素的地址。
二.a+i是什么?
- 根据前面的知识,我们大概可以猜出a+1是地址,那么它所指向的内存是什么呢?
- 前文提到:a指向大数组的第一个元素:小数组a[0] 的第一个元素a[0][0]。那么a+1则指向大数组的第二个元素:小数组a[1] 的第一个元素a[1][0],或者说a+1是小数组a[1]的首元素的地址。从内存来看,假定内存名为a的内存(存放
&a[0][0])的地址是2000,a+1的值则是2000+4×4=2016。- 以此类推,a+i指向大数组的第i+1个元素:小数组a[i] 的第一个元素a[i][0]。
- 由此可以得出以下重要的等价关系:
1.a+i与
&a[i](实际上是&a[i][0])等价。2.
*(a+i)与a[i]等价(二者都是地址)。
三.a[i]+j是什么?
- 在一维数组
int a[3]={1,2,3}中,a+1(指针:存放a[1]的地址)指向a[1],即1.a+1与&a[1]等价。2.*(a+1)与a[1]等价。- 那么在一维数组中,1.a+i与&a[i]等价。 2.
*(a+i)与a[1]等价。- 回到我们的二维数组,a[i]是小数组名,同理有如下等价关系,请务必记住:
1.a[i]+j与
&a[i][j]等价2.
*(a[i]+j)与a[i][j]等价3.由上文二.2和三.2的等价关系可得:
*(*(a+i)+j)与a[i][j]等价。
理解上面很抽象的内容是不是很痛苦呢,我在接触指针的时候也绕了很多弯路,这里给大家的建议还是放慢脚步,多回归课本,不急于求成。
*与&的故事
- 上面提到,(a+i)管理着小数组,它的元素小数组a[0],a[1],a[2]管理着整形变量元素a[0][0]...a[2][3]。
- 在“指针”这家公司,有普通的“职员”:整形变量。“小管家”:
a[i]。还有“总管”:(a+i)。- 那么,
*有指向的意思,如果在(a+i)前面加上*,它就由管理小数组的“总管”,变成了管理整形变量元素的“小管家”,这次改变相当于“降级”了。- 同理,在管理整形变量元素的“小管家”
a[i]前面加上&,就升级为“总管”,管理着小数组。- 升降级这些变化都是在指针公司里面进行的,大家在职位变化完的第二天,依然以指针员工的身份愉快的工作着。
- 通过以上的一则故事,我们可以知道这些东西:
1.在(a+i)(指针)前面加上一个
*,*(a+i)就是a[i](指针),那么*(a+i)+j就是a[i]+j,指向a[i][j]。2.在
a[i](指针)前面加上一个&,&a[i]就是(a+i)(指针),那么&a[i]+j就是(a+i)+j,指向a[i+j]。3.以上是指针到指针的变化。
我们再回到二维数组指针的基类型
在多维数组中,指针变量有两种类型:1.指向数组元素的指针变量和2.指向n个元素组成的一维数组的指针变量
关于指向数组元素的指针变量,相信大家在复习前面的内容以后都好理解,这里来看一个例子。
- 代码1:
#include<stdio.h>
int main()
{
int *p;
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int i;
p=a[0];/*p初始化指向a[0][0]*/
for(p=a[0];p<a[0]+12;p++)
printf("%d ",*p);
return 0;
}
输出结果:
上面是按顺序输出数组各元素的值,这里介绍如何输出某个指定的数值元素。
假设有一个n×m的二维数组,计算a[i][j]在数组中的相对位置:i*m+j其中m是n×m矩阵的列数。
那么a[i][j]的地址就等于&a[0][0]+i*m+j
- 代码2:
#include<stdio.h>
int main()
{
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *p=a[0];/*或者:int *p=&a[0][0]*/
/*要求:输出a[1][2]的值*/
/*法1:printf("%d",*(a[1]+2));*/
/*法2:printf("%d",*(*(a+1)+2));*/
printf("%d",*(p+1*4+2));/*法3:利用相对位置*/
return 0;
}
再来看指向一维数组的指针变量。
- 代码3(片段):
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*p1)[4];/*这个定义指针的方式在下文会提到,注意它和定义指针数组(int *p1[4])的区别*/
int *p2;
p1=a;/*指针p1指向一维数组a[0]*/
p2=a[0];/*指针p2指向数组元素a[0][0]*/
在上面的代码3里,p1是指向一维数组
a[0]的指针,p2是指向数组元素a[0][0]的指针。a[0],a[1],a[2],p2的基类型是
int *型(指向整形变量)
而二维数组名a,p1的基类型是int(*)[4](指向包含四个整形元素的一维数组)。下面介绍指向一维数组指针的定义方法。
指向一维数组指针的定义
上文中的代码1定义的指针变量p是指向变量或者数组元素,现在定义指向一维数组的指针。
- 代码1(改):输出数组元素的值。
#include<stdio.h>
int main()
{
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*p)[4];/*定义指向一维数组的指针p*/
int i,j;
p=a;/*不能写成p=&a;*/
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%d ",*(*(p+i)+j));
printf("\n");
}
return 0;
}
输出结果:
int (*p)[4];表示定义指针变量p,指向包含四个元素的一维数组。这里(*p)两边的括号不能去掉。如果去掉的话,*p[4]方括号运算顺序级别高,p先和方括号结合再与前面的*结合,*p[4]就是指针数组。有关指针数组详见下文。此时,代码段
p=a;把a[0](一维数组)的地址赋值给p(该一维数组的初始地址&a[0][0])。
- 这里p只能指向一维数组而不能指向一维数组里的元素,来看反例:
#include<stdio.h>
int main()
{
int a[4]={1,2,3,4};
int (*p)[4];
p=a;/*错误*/
printf("%d",(*p)[3]);/*输出a[3]*/
return 0;
}
编译错误,我们定义指向一维数组的指针p,p只能指向一维数组,而这里的错误是把一维数组a的首元素地址赋值给了p,从而p指向了a[0],即指向了整形元素。
正确的赋值方法应该是:
p=&a;关于*和&请看上文。
《C语言程序设计》指针篇<二>的更多相关文章
- 快速上手系列-C语言之指针篇(一)
快速上手系列-C语言之指针篇(一) 浊酒敬风尘 发布时间:18-06-2108:29 指针的灵活运用使得c语言更加强大,指针是C语言中十分重要的部分,可以说指针是C语言的灵魂.当然指针不是万能的,但没 ...
- 瘋子C语言笔记(指针篇)
指针篇 1.基本指针变量 (1)定义 int i,j; int *pointer_1,*pointer_2; pointer_1 = &i; pointer_2 = &j; 等价于 i ...
- Java语言程序设计-助教篇
1. 给第一次上课(软件工程)的老师与助教 现代软件工程讲义 0 课程概述 给学生:看里面的第0个作业要求 2. 助教心得 美国视界(1):第一流的本科课堂该是什么样?(看里面的助教部分) 助教工作看 ...
- 0031 Java学习笔记-梁勇著《Java语言程序设计-基础篇 第十版》英语单词
第01章 计算机.程序和Java概述 CPU(Central Processing Unit) * 中央处理器 Control Unit * 控制单元 arithmetic/logic unit /ə ...
- C语言重点——指针篇(一文让你完全搞懂指针)| 从内存理解指针 | 指针完全解析
有干货.更有故事,微信搜索[编程指北]关注这个不一样的程序员,等你来撩~ 注:这篇文章好好看完一定会让你掌握好指针的本质 C语言最核心的知识就是指针,所以,这一篇的文章主题是「指针与内存模型」 说到指 ...
- C\C++语言重点——指针篇 | 为什么指针被誉为 C 语言灵魂?(一文让你完全搞懂指针)
本篇文章来自小北学长的公众号,仅做学习使用,部分内容做了适当理解性修改和添加了博主的个人经历. 注:这篇文章好好看完一定会让你掌握好指针的本质! 看到标题有没有想到什么? 是的,这一篇的文章主题是「指 ...
- Java语言程序设计(基础篇)第一章
第一章 计算机.程序和Java概述 1.1 引言 什么是程序设计呢? 程序设计就是创建(或者开发)软件,软件也称为程序. 1.2 什么是计算机 计算机是存储和处理数据的电子设备,计算机包括硬件(har ...
- C语言程序设计(十二) 结构体和共用体
第十二章 结构体和共用体 当需要表示复杂对象时,仅使用几个基本数据类型显然是不够的 根本的解决方法是允许用户自定义数据类型 构造数据类型(复合数据类型)允许用户根据实际需要利用已有的基本数据类型来构造 ...
- Java语言程序设计(基础篇)第二章
第二章 基本程序设计 2.2 编写简单的程序 1.变量名尽量选择描述性的名字(descriptive name). 2.实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示.因此,实数也称为浮点 ...
随机推荐
- iOS10网络权限数据
参考地址:1.http://www.cocoachina.com/ios/20180723/24274.html https://blog.csdn.net/wang_bo_justone/art ...
- XMind思维导图主题操作要点
- 5分钟带你入门vuex(vue状态管理)
如果你之前使用过vue.js,你一定知道在vue中各个组件之间传值的痛苦,在vue中我们可以使用vuex来保存我们需要管理的状态值,值一旦被修改,所有引用该值的地方就会自动更新,那么接下来我们就来学习 ...
- rsync 配置详解
安装 [root@localhost ~]# yum install -y rsync [root@localhost ~]# systemctl start rsyncd [root@localho ...
- easyDialog参数配置说明
easyDialog不依赖框架,使用起来很简单,只要引入easydialog.js文件就可以使用了: // 引入easyDialog <script src="easydialog.j ...
- Object之equals和hashCode
译者注 :你可能会觉得Java很简单,Object的equals实现也会非常简单,但是事实并不是你想象的这样,耐心的读完本文,你会发现你对Java了解的是如此的少.如果这篇文章是一份Java程序员的入 ...
- 网络流-最大流 Dinic模板
#include <bits/stdc++.h> using namespace std; #define MP make_pair #define PB push_back #defin ...
- python 不同集合上元素的迭代 chain()
itertools.chain()可以接受一个可迭代对象列表作为输入,并返回一个迭代器,有效的屏蔽掉在多个容器中迭代细节 >>> from itertools import chai ...
- WSDL解析
背景 前面我们介绍过利用javassist动态生成webservice,这种方式可以使得我们系统通过页面配置动态发布webservice服务,做到0代码开发发布北向接口.进一步思考,我们如何0代码开发 ...
- python3.4学习笔记(十) 常用操作符,条件分支和循环实例
python3.4学习笔记(十) 常用操作符,条件分支和循环实例 #Pyhon常用操作符 c = d = 10 d /= 8 #3.x真正的除法 print(d) #1.25 c //= 8 #用两个 ...


