《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.实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示.因此,实数也称为浮点 ...
随机推荐
- Shell初学(一)hello world
精简: 1.创建:可以使用 vi/vim 命令来创建文件如: test.sh ,扩展名并不影响脚本执行,写什么都可以. 2.hello_world: #!/bin/bash ...
- node.js 开发桌面程序, 10个令人惊讶的NodeJS开源项目
用 node-webkit 开源框架. 做企业站,杠杠地 包括电子书和支付宝系统都是node开发的,. 接收传感器发送的数据再运算...对水泵.风机.空调这些硬件进行远程控制. 细数10个令人惊讶的N ...
- Biorhythms(中国剩余定理)
http://shuxueshi.jie.blog.163.com/blog/static/13611628820104179856631/ 这篇博客写的很棒! #include<stdio.h ...
- R之ddlpy函数学习[转载]
转自:https://www.cnblogs.com/aloiswei/p/6032513.html 1.函数 ddply(.data, .variables, .fun = NULL, ..., . ...
- ubuntu开发环境下eclipse的alt+/自动补全功能不能用
解决方法:windows ---preferences---General---keys ,把在搜索框中搜Word Completion,把该快捷键unbind,然后给content assist 绑 ...
- pod 的warning一定要注意消除,要不然你的pod配置有问题无法导入
and the CHANGELOG for this version http://git.io/BaH8pQ. Downloading dependencies Using BlocksKit (2 ...
- 机器学习理论基础学习1——频率派 VS 贝叶斯派
频率派 贝叶斯派 theta是个未知的常量,X是随机变量, theta是个随机变量,X是随机变量 MLE最大似然估计 MAE最大后验概率 统计机器学习,优化问题 1)建立模型.概率 2)定义损失函数 ...
- iOS手机应用开发原型模板及开发流程
在开始做一个项目的时候,究竟需要准备些什么前期工作呢,在外包公司中,又是一个怎么样的开发步骤呢?下面,按照我的理解,说一下. 项目流程图 看上图,从左往右,如果我们仅是一名开发工程师,只需要关心上图中 ...
- Git 全局设置
Git 全局设置: git config --global user.name "ASxx" git config --global user.email "123456 ...
- [转]使用C#开发ActiveX控件全攻略
前言: 这段时间因为工作的需要,研究了一下ActiveX控件.总结如下: 先说说ActiveX的基本概念. 根据微软权威的软件开发指南MSDN(Microsoft Developer Network) ...