关于用二维数组调用函数的寻址(多维数组和指针)

本文是关于C语言多维数组与指针的一个例子。直接上代码,我们来分析程序运行结果

 

#include <stdio.h>

#include <stdlib.h>

//使用数组表达式访问元素

void output(int** a)

{

        int i;

        int j;

       for (i=0; i<2; i++)

       {

              for (j=0; j<2; j++)

              {

                     printf("%d ",a[i][j]);

              }

              printf("/n");

       }

}

//使用指针表达式访问元素

void output1(int** a)

{

       int i;

       int j;

       for (i=0; i<2; i++)

       {

              for (j=0; j<2; j++)

              {

                     printf("%d ",*((int*)a+i*2+j));

              }

              printf("/n");

       }

}

void main()

{

       //b是数组

       int b[2][2]={{10,10},{10,10}};

       //c是指针

       int** c;

       c=(int**)malloc(sizeof(int*)*2);

       c[0]=(int*)malloc(sizeof(int)*2);

       c[1]=(int*)malloc(sizeof(int)*2);//注意:c[0]和c[1]指向的内存并非收尾相接!!!

       c[0][0]=10;

       c[0][1]=10;

       c[1][0]=10;

       c[1][1]=10;

       //运行时发生访问错误

       printf(" array   array:/n");

       output((int**)b);

       //正确

       printf("/n array    other:/n");

       output1((int**)b);

       //正确

       printf("/n other   array:/n");

       output(c);

       //“乱码”, 即非预期输出结果

       printf("/n other   other:/n");

       output1(c);

}

                           

 

 

 

 

 

 

                          

分析

output((int**)b);

该调用在进行参数传递的时候,试图用b的值(数组首元素的地址)初始化一个指向指针的指针变量(即形参a),并以数组下标的形式访问数组元素,程序将发生访问错误并终止执行。这是因为系统(关系到编译器、连接器甚至装载器的实现)无法单纯依据一个指向指针的指针变量来定位对应的数组成员,系统并不知道指针所指向的内存模型,如果以数组下标的形式访问内存,就会发生未定义错误。

我们可以认为系统把b[1][1]解析为*(b+n*1+1),其中n为数组b的第二维的长度。显然,在上述的情形下,系统并不知道n的值,甚至不知道n的存在。

output1((int**)b);

基于上述的分析,我们不难理解为什么该调用可以得到预期结果,因为我们使用指针表达式的形式来访问内存。

output(c);

在分析该调用的执行过程之前,我们有必要先观察c是如何被构造出来的。考查这个构造过程并结合我们的内存模型示意图,可知我们一共为c开辟了一下内存空间:两个指针变量空间(连续的)、两个整型变量空间(连续的)、两个整型变量空间(连续的)。关键在于两组整型变量空间不一定连续(因为它们是动态申请分配的),即实际存放整型数据的内存不一定连续,这就是它和数组b的关键区别所在(b被声明为一个数组,内存是连续的)。所以在这里我们把c看成是一个二维数组,无论在物理上还是逻辑上都是不正确的。

现在我们回过头来分析output(c)调用。既然c不能看成是一个二维数组,它对于的内存在物理上和逻辑上都是不连续的,为什么我们以类似“c[1][1]”的形式访问内存的时候,竟然可以得到预期的结果呢?这是一个疑问,但是更大的困惑在于我们调用output1(c),即以指针表达式的形式(系统就是以这样的方式访问数组成员的!)访问内存,却得到一堆“乱码”?

到目前为止,情况好像比较混乱。其实有了上面的分析基础,再作进一步分析,我们就可以理解为什么这个程序是这种行为。

内存是不连续的,所以在假定内存连续的逻辑下,调用output(c),使用指针表达式来访问元素,不会发生错误,因为系统只是根据指针表达式的运算结果寻找,所以有可能不会发生访问错误,但是读出来的内容却是非预期的。

另一方面,对于output1(c)调用,通过数组表达式来访问元素,并不意味着内存模型必定连续。因为c[1]本身就是一个数组,所以c[1][1]可以看成是访问数组c[1]中的第二个元素。

总结

这个例子来源于最近debug某个程序,一直找不到问题的根源。在确定了算法和其他逻辑没有问题之后,缩小问题的范围,于是简化出这个例子,顺利找出并解决了遇到的问题。

其实是粗心的问题,对自己所做的事情没有彻底清醒的认识,即没有做到“知道自己在做什么”!

把该问题记录在案,一方面可以理清对C语言“数组和指针”关系的认识,另一方面,也是更重要的——警示自己做事细心。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值