您的位置:首页 > 其它

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

2010-11-21 00:46 344 查看
本文是关于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语言“数组和指针”关系的认识,另一方面,也是更重要的——警示自己做事细心。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: