通过指针引用多维数组

如何理解二维数组元素的地址?

要知道,这本书用了整整两页的内容来讲解这方面的知识,从这里足以看出来理解通过指针来引用二维数组是一件比较麻烦的事情,但是我认为理解并不难。

  • 什么是二维数组?

    举个例子: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语言程序设计》指针篇<二>的更多相关文章

  1. 快速上手系列-C语言之指针篇(一)

    快速上手系列-C语言之指针篇(一) 浊酒敬风尘 发布时间:18-06-2108:29 指针的灵活运用使得c语言更加强大,指针是C语言中十分重要的部分,可以说指针是C语言的灵魂.当然指针不是万能的,但没 ...

  2. 瘋子C语言笔记(指针篇)

    指针篇 1.基本指针变量 (1)定义 int i,j; int *pointer_1,*pointer_2; pointer_1 = &i; pointer_2 = &j; 等价于 i ...

  3. Java语言程序设计-助教篇

    1. 给第一次上课(软件工程)的老师与助教 现代软件工程讲义 0 课程概述 给学生:看里面的第0个作业要求 2. 助教心得 美国视界(1):第一流的本科课堂该是什么样?(看里面的助教部分) 助教工作看 ...

  4. 0031 Java学习笔记-梁勇著《Java语言程序设计-基础篇 第十版》英语单词

    第01章 计算机.程序和Java概述 CPU(Central Processing Unit) * 中央处理器 Control Unit * 控制单元 arithmetic/logic unit /ə ...

  5. C语言重点——指针篇(一文让你完全搞懂指针)| 从内存理解指针 | 指针完全解析

    有干货.更有故事,微信搜索[编程指北]关注这个不一样的程序员,等你来撩~ 注:这篇文章好好看完一定会让你掌握好指针的本质 C语言最核心的知识就是指针,所以,这一篇的文章主题是「指针与内存模型」 说到指 ...

  6. C\C++语言重点——指针篇 | 为什么指针被誉为 C 语言灵魂?(一文让你完全搞懂指针)

    本篇文章来自小北学长的公众号,仅做学习使用,部分内容做了适当理解性修改和添加了博主的个人经历. 注:这篇文章好好看完一定会让你掌握好指针的本质! 看到标题有没有想到什么? 是的,这一篇的文章主题是「指 ...

  7. Java语言程序设计(基础篇)第一章

    第一章 计算机.程序和Java概述 1.1 引言 什么是程序设计呢? 程序设计就是创建(或者开发)软件,软件也称为程序. 1.2 什么是计算机 计算机是存储和处理数据的电子设备,计算机包括硬件(har ...

  8. C语言程序设计(十二) 结构体和共用体

    第十二章 结构体和共用体 当需要表示复杂对象时,仅使用几个基本数据类型显然是不够的 根本的解决方法是允许用户自定义数据类型 构造数据类型(复合数据类型)允许用户根据实际需要利用已有的基本数据类型来构造 ...

  9. Java语言程序设计(基础篇)第二章

    第二章 基本程序设计 2.2 编写简单的程序 1.变量名尽量选择描述性的名字(descriptive name). 2.实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示.因此,实数也称为浮点 ...

随机推荐

  1. python的时间差计算

    import time start = time.clock() #当中是你的程序 elapsed = (time.clock() - start) print("Time used:&qu ...

  2. SVN出现xcrun: error: invalid active developer path(Mac)

    Mac升级了系统,配置PHPStorm的SVN,出现如下错误: 具体提示的内容是:xcrun: error: invalid active developer path (/Library/Devel ...

  3. 【Cocos2dx 3.3 Lua】SpriteBatchNode和SpriteFrameCache使用

    精灵帧缓存类 一.SpriteFrameCache     精灵帧缓冲类SpriteFrameCache用于存储精灵帧,SpriteFrameCache是一个单例模式,不属于某一个精灵,是所有精灵共享 ...

  4. HTTP 教程

    HTTP 简介 HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准. HTTP是一个基于 ...

  5. [LeetCode] 64. Minimum Path Sum_Medium tag: Dynamic Programming

    Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which ...

  6. testng入门教程4用TestNG执行case

    使用TestNG类执行测试用例.这个类的主入口点在TestNG的框架运行测试.用户可以创建自己的TestNG的对象,并调用它以许多不同的方式: 在现有的testng.xml 合成testng.xml, ...

  7. c++中的构造(包括移动),赋值(包括移动),析构详解

    这五种操作:构造(包括移动),赋值(包括移动),析构其实就是定义了对一个对象进行构造,赋值,析构时的行为.理解这些行为并不复杂,复杂的是理解在继承下这些行为的表现.需要注意的是他们并不会被继承(传统意 ...

  8. linux常用命令:ss 命令

    ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信 ...

  9. 页面点击,不是a标签也会刷新原因

    页面点击,不是a标签也会刷新原因 点击事件冒泡,触发了a链接导致整个页面刷新了.直接阻止 事件冒泡即可 例子: $("tr .am-text-danger").click(func ...

  10. 谈话准备.xmind 思维导图模版

    谈话准备.xmind 思维导图模版,谈话准备.xmind.zip 谈话准备 目标 1…… 2…… 3…… 影响决定因素 进程 经费 策略 问题 客户 竞争对手 竞争对手 潜在 现存 问题 个性 团队 ...