http://c.biancheng.net/cpp/html/477.html

多维数组与多级指针也是初学者感觉迷糊的一个地方。超过二维的数组和超过二级的指针其实并不多用。如果能弄明白二维数组与二级指针,那二维以上的也不是什么问题了。所以本节重点讨论二维数组与二级指针。

一、二维数组

1、假想中的二维数组布局
我们前面讨论过,数组里面可以存任何数据,除了函数。下面就详细讨论讨论数组里面存数组的情况。Excel 表,我相信大家都见过。我们平时就可以把二维数组假想成一个excel表,比如:
   char a[3][4];

2、内存与尺子的对比
实际上内存不是表状的,而是线性的。见过尺子吧?尺子和我们的内存非常相似。一般尺子上最小刻度为毫米,而内存的最小单位为1
个byte。平时我们说32 毫米,是指以零开始偏移32 毫米;平时我们说内存地址为0x0000FF00
也是指从内存零地址开始偏移0x0000FF00 个byte。既然内存是线性的,那二维数组在内存里面肯定也是线性存储的。实际上其内存布局如下图:

以数组下标的方式来访问其中的某个元素:a[i][j]。编译器总是将二维数组看成是一个一维数组,而一维数组的每一个元素又都是一个数组。a[3]这个一维数组的三个元素分别为:
a[0],a[1],a[2]。每个元素的大小为sizeof(a[0]),即sizof(char)*4。由此可以计算出a[0],a[1],a[2]
三个元素的首地址分别为& a[0],& a[0]+ 1*sizof(char)*4,& a[0]+
2*sizof(char)*4。亦即a[i]的首地址为& a[0]+
i*sizof(char)*4。这时候再考虑a[i]里面的内容。就本例而言,a[i]内有4个char
类型的元素,其每个元素的首地址分别
为&a[i],&a[i]+1*sizof(char),&a[i]+2*sizof(char)&a[i]+3*sizof(char),
即a[i][j]的首地址为&a[i]+j*sizof(char)。再把&a[i]的值用a
表示,得到a[i][j]元素的首地址为:a+ i*sizof(char)*4+
j*sizof(char)。同样,可以换算成以指针的形式表示:*(*(a+i)+j)。

经过上面的讲解,相信你已经掌握了二维数组在内存里面的布局了。下面就看一个题:
#include <stdio.h>
intmain(int argc,char * argv[])
{
   int a [3][2]={(0,1),(2,3),(4,5)};
   int *p;
   p=a [0];
   printf("%d",p[0]);
}
问打印出来的结果是多少?

很多人都觉得这太简单了,很快就能把答案告诉我:0。不过很可惜,错了。答案应该是1。如果你也认为是0,那你实在应该好好看看这个题。花括号里面嵌套的是小括号,而不是花括号!这里是花括号里面嵌套了逗号表达式!其实这个赋值就相当于
   int a [3][2]={ 1, 3,5};
所以,在初始化二维数组的时候一定要注意,别不小心把应该用的花括号写成小括号
了。

3、&p[4][2] - &a[4][2]的值为多少?
上面的问题似乎还比较好理解,下面再看一个例子:
   int a[5][5];
   int (*p)[4];
   p = a;
问&p[4][2] - &a[4][2]的值为多少?

这个问题似乎非常简单,但是几乎没有人答对了。我们可以先写代码测试一下其值,然后分析一下到底是为什么。在Visual C++6.0 里,测试代码如下:
intmain()
{
   int a[5][5];
   int (*p)[4];
   p = a;
   printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4][2]);
   printf("%p,%d\n",&p[4][2] - &a[4][2],&p[4][2] - &a[4][2]);
   return 0;
}
经过测试,可知&p[4][2] - &a[4][2]的值为-4。这到底是为什么呢?下面我们就来分析一下:前面我们讲过,当数组名a
作为右值时,代表的是数组首元素的首地址。这里的a 为二维数组,我们把数组a 看作是包含5 个int
类型元素的一维数组,里面再存储了一个一维数组。

如此,则a 在这里代表的是a[0]的首地址。a+1 表示的是一维数组a 的第二个元素。a[4]表示的是一维数组a 的第5
个元素,而这个元素里又存了一个一维数组。所以&a[4][2]表示的是&a[0][0]+4*5*sizeof(int) +
2*sizeof(int)。

根据定义,p 是指向一个包含4 个元素的数组的指针。也就是说p+1 表示的是指针p 向后移动了一个“包含4 个int 类型元素的数组”。这里1
的单位是p 所指向的空间,即4*sizeof(int)。所以,p[4]相对于p[0]来说是向后移动了4 个“包含4 个int
类型元素的数组”,即&p[4]表示的是&p[0]+4*4*sizeof(int)。由于p
被初始化为&a[0],那么&p[4][2]表示的是&a[0][0]+4*4*sizeof(int)+2*
sizeof(int)。

再由上面的讲述,&p[4][2] 和&a[4][2]的值相差4 个int 类型的元素。现在,上面测试出来的结果也可以理解了吧?其实我们最简单的办法就是画内存布局图:

这里最重要的一点就是明白数组指针p 所指向的内存到底是什么。解决这类问题的最好办法就是画内存布局图。

二、二级指针

1、二级指针的内存布局
二级指针是经常用到的,尤其与二维数组在一起的时候更是令人迷糊。例如:
   char **p;
定义了一个二级指针变量p。p 是一个指针变量,毫无疑问在32 位系统下占4 个byte。

它与一级指针不同的是,一级指针保存的是数据的地址,二级指针保存的是一级指针的地址。下图帮助理解:

我们试着给变量p 初始化:
A)
p = NULL;
B)
char *p2; p = &p2;
任何指针变量都可以被初始化为NULL(注意是NULL,不是NUL,更不是null),二级指针也不例外。也就是说把指针指向数组的零地址。联想到前面我们把尺子比作内存,如果把内存初始化为NULL,就相当于把指针指向尺子上0 毫米处,这时候指针没有任何内存可用。

当我们真正需要使用p 的时候,就必须把一个一级指针的地址保存到p 中,所以B)的赋值方式也是正确的。

给p 赋值没有问题,但怎么使用p 呢?这就需要我们前面多次提到的钥匙(“*”)。
第一步:根据p 这个变量,取出它里面存的地址。
第二步:找到这个地址所在的内存。
第三步:用钥匙打开这块内存,取出它里面的地址,*p 的值。
第四步:找到第二次取出的这个地址。
第五步:用钥匙打开这块内存,取出它里面的内容,这就是我们真正的数据,**p 的值。

我们在这里用了两次钥匙(“*”)才最终取出了真正的数据。也就是说要取出二级指针所真正指向的数据,需要使用两次两次钥匙(“*”)。

至于超过二维的数组和超过二维的指针一般使用比较少,而且按照上面的分析方法同样也可以很轻松的分析明白,这里就不再详细讨论。读者有兴趣的话,可以研究研究

[转] C语言多维数组与多级指针的更多相关文章

  1. C语言二维数组中的指针问题

    #include "stdio.h" void main() { int a[5][5]; int i,j; for (i=0;i<5;i++) { for (j=0;j&l ...

  2. c语言二维数组传递

    c语言二维数组传递,目前我总结三种方法,以及纠正一个不能使用的方法 /********************************* * 方法1: 第一维的长度可以不指定 * * 但必须指定第二维 ...

  3. [语法]C语言中二维数组做输入参数

    C语言中二维数组做输入参数时, 可以同时指定各维长度, 可以只指定第二维的长度, 不可以只指定第一维的长度, 不可以各维长度都不指定. 一句话总结:要指定至少指定第二维,都不指定是不行的. 具体栗子如 ...

  4. C语言中二维数组如何申请动态分配内存

    C语言中二维数组如何申请动态分配内存: 使用malloc函数,先分配第一维的大小,然后再循环分配每一维的大小 #include <stdio.h> #include <malloc. ...

  5. C语言——二维数组

    目录 二维数组 一.二维数组的定义 二.二维数组的初始化 三.通过赋初值定义二维数组的大小 四.二维数组与指针 二维数组 一.二维数组的定义 类型名 数组名[ 常量表达式1 ][ 常量表达式2 ] i ...

  6. 关于c语言二维数组与指针的个人理解及处理办法。

    相信大家在学习C语言时,对一维数组和指针的理解应该是自信的,但是,我在学习过程中,看到网上一些博文,发现即便是参加工作的一些专业编程人员,突然碰到二维数组和指针的问题时,也可能会遇到难以处理的诡异问题 ...

  7. C语言多维数组的地址

    设有整型二维数组a[3][4]如下: 0   1   2   3 4   5   6   7 8   9  10  11  它的定义为:     int a[3][4]={{0,1,2,3},{4,5 ...

  8. C语言 二维数组复制、清零及打印显示

    #include <stdlib.h> #include <stdio.h> #include <string.h> //二维整型数组打印显示 ],int row, ...

  9. C语言二维数组作业

    一.PTA实验作业 题目1:7-3 出生年 1. 本题PTA提交列表 2. 设计思路 1.声明一个函数different()用来计算一个年份的不同数字个数 2.定义y(y是来计算符合要求的年份的量), ...

随机推荐

  1. QQ弹窗代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. PHP下编码转换函数mb_convert_encoding与iconv的使用说明

    mb_convert_encoding这个函数是用来转换编码的. 不过英文一般不会存在编码问题,只有中文数据才会有这个问题.比如你用Zend Studio或Editplus写程序时,用的是gbk编码, ...

  3. const关键字与指针

    const与指针在一起的几种情况. const int *p1; //表示p1本身不是const,指向的变量是const. const *int p2; //语法错误 int const *p3; / ...

  4. 练习2 D 题- 第几天?

    Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   Description 给定一个日 ...

  5. Asp.Net MVC5 格式化输出时间日期

    刚好用到这个,网上找的全部是输出文本框内容的格式化日期时间 而我需要只是在一个表格中的单元个中输出单纯的文字 最后在MSDN上找到 HtmlHelper.FormatValue 方法 public s ...

  6. 转:php中实现精确设置session过期时间的方法

    原文来自于:http://www.jb51.net/article/52309.htm 大多数据情况下我们对于session过期时间使用的是默认设置的时间,而对于一些有特殊要求的情况下我们可以设置一下 ...

  7. Unity3d 与IOS 相互调用

    Unity3d 与IOS 相互调用 @灰太龙 群63438968 我用的Unity3d 4.2版本,这一节说一下IOS与U3D的交互! 首先在U3D中写个方法:这个时候导出为ios代码必须是真机,模拟 ...

  8. BZOJ1044: [HAOI2008]木棍分割

    1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1580  Solved: 567[Submit][Statu ...

  9. 有两个指针pa,pb分别指向有两个数,a,b,请写一个函数交换两个指针的指向,也就是让pa指向b,让pb指向a

    题目:有两个指针pa,pb分别指向有两个数,a,b,请写一个函数交换两个指针的指向,也就是让pa指向b,让pb指向a,具体实现如下: #include<stdlib.h> #include ...

  10. 数据结构(主席树):COGS 2213. K个串

    2213. K个串 ★★★★   输入文件:bzoj_4504.in   输出文件:bzoj_4504.out   简单对比时间限制:20 s   内存限制:512 MB [题目描述] 兔子们在玩k个 ...