通过指针引用多维数组

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

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

  • 什么是二维数组?

    举个例子: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. XtraBackup完整备份与增量备份的原理

    MySQL数据库实现备份的操作包括完整备份和增量备份等,本文我们主要介绍一下增量备份和完整备份的原理,接下来我们就一起来了解一下这部分内容. 完整备份的原理: 对于InnoDB,XtraBackup基 ...

  2. [py][mx]django静态文件目录配置

    使用TemplateView直接返回html from django.views.generic import TemplateView urlpatterns = [ path('',Templat ...

  3. JSP表单提交与接收

    JSP表单提交与接收 在Myeclipse中新建web project,在webroot中新建userRegist1.jsp,代码如下 <%@ page contentType="te ...

  4. jquery.dataTables表格中的内容怎么设置让它不自动换行

    在table中增加 style="white-space: nowrap;" ,这样会撑大td.会出现滚动条. 其他内容配置:每列宽度: "aoColumnDefs&qu ...

  5. matplotlib —— 调整坐标轴

    import matplotlib.pyplot as plt import numpy as np # 绘制普通图像 x = np.linspace(-1, 1, 50) y1 = 2 * x + ...

  6. 为什么size_t重要?

    参见 http://en.cppreference.com/w/cpp/types/size_t size_t其实与uintptr_t一个道理.就是一个东西.指针其实就是寻址,与地址总线位数一致,编译 ...

  7. 第七章之main函数和启动例程

    main函数和启动例程 为什么汇编程序的入口是_start,而C程序的入口是main函数呢?本节就来解释这个问题.在讲例 18.1 “最简单的汇编程序”时,我们的汇编和链接步骤是: $ as hell ...

  8. arcgis中加载google在线地图

    打开arcmap——文件——arcgis online ——搜索china maps 选择china

  9. Java中如何优雅地删除List中的元素

    在工作中的许多场景下,我们都会使用到List这个数据结构,那么同样的有很多场景下需要删除List中的某一个元素或某几个元素,那么我们该如何正确无误地删除List中的元素的,今天我来教大家三种方式. 前 ...

  10. html5<embed>的完整属性

    问题起因:网页中插入Flash,看了代码,然后呢,小小的学习下 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000& ...