分类: C/C++
2010-03-16 21:17:51
二级指针和二维数组
author: fengxz
先看下面一个简单例子:
int main()
{
char *onedp = NULL; //one dimension array
char **twodp = NULL; //two dimension array
char a[3][16] = {"China_and_me", "c++", "It is a desk"};
int i = 0;
twodp = (char **)a;
for(i = 0; i < 3; i++)
{
printf("%s\n",a[i]);
printf("%s\n", *(twodp + i));
}
return 0;
}
在以上程序中,twodp 是一个二级指针,a[3][16]是一个二维数组。
for本要循环输出a[3][16]中的每一个字符串,但是遗憾的是,在执行printf("%s\n", *(twodp + i))时程序就down了。
下面看一下其中用到了那些知识:
1) 以一维数组角度看待二维数组。
对于a[3][16],这样来认识:首先把a作为一维数组看待,不过这个数组的元素仍是数组,即有3个数组为a的元素:a[0],a[1],a[2]。因此,a+1就是跨越一个行,所以a也称为行地址。关于行指针后面叙述。
而a[i](i=0,1,2)又是一个一维数组,他的元素为a[i][j](j=0,1,2,...15),所以a[i]+1跨越a[3][16]的一个元素大小。
2) 应该要知道一点,指针变量中存放的是某个地址的值。
所以一维指针和二维指针变量的值,即onedp和twodp值都是某个地址,一维指针和二维指针变量不同的是,对二维指针变量操作:*twodp仍然是一个地址,而*onedp就是代表one所指的变量了。
这样看来,对上面的程序,twodp = (char **)a;这样twodp就指向二维数组的首地址,而这个首地址指向了字符C,当i为0时,twodp + i存放的是字符C的地址,那么*(twodp + i)的值就是字符C了,而字符C的ASCII码的16进制值是0x63,所以*(twodp + i)是0x63。注意,不要忘了twodp是一个二维指针,所以*(twodp + i)仍是一个地址,不幸,该值0x63却是内存中我们所无法访问的空间。这样我们就不难理解上面程序down的原因了。知道了原因,就离解决方法不远了,不过在说解决方法之前,我们先看另一个问题
3)对指针的运算,均是以指针所代表的类型为单位进行。
如果i=2,twodp = 0x0012fe90那么twodp + i是多少?
如果你答0x0012fe92,那就错了。
因为对指针的运算,均是以指针所代表的类型为单位进行。对于二级指针twodp,它的一级指针*twodp仍是指针类型,无论是char指针,还是其他指针,只要是指针类型,就占有4个字节(32位机)。所以twodp + i就是0x0012fe90 + i*4,当i=1时为0x0012fe94!
4)“行指针”概念
看一个例子:
int (*p)[16];
它定义了这样一个指针:这个指针指向一个一维数组,这个一维数组共有16个元素。
如果这样使用:
int a1[16];
p = a1;
那么p+1将发生越界,因为对p而言,它以行为单位进行。它每次走一行,一行是它的最小单位。对于a1[16],一行就是16个元素。每个元素4个字节,所以每一次p+1将跨越64个字节。行指针用在二维数组表现的很充分。因为二维数组可以看成一维数组的数组。对于二维数组a[3][16],共有3行。每一行又是含有16个元素的一维数组。从刚才上面对二位数组分析,二位数组名就是该数组第0行首地址,就是行地址。但是一定要注意a不是行指针,只是一个常量。
注意定义行指针时保持和二维数组的第二维一致!因为行指针移动按照这个“维”进行的。
有了以上知识,我们就可以方便操作二维数组了。
对于开始的例子,为了输出二维数组a[3][16]的每个字符串,以下几种方法:
1)设一个行指针。
char (*linep)[16]; // line type pointer
linep = a[0];
for(i = 0; i < 3; i++)
{
printf("%s\n",linep + i);
}
2)直接使用二级指针,不过一定注意二级指针的计算
twodp = (char **)a;
for(i = 0; i < 3; i++)
{
printf("%s\n", twodp + i*4);
}
这里一定要知道为什么是i与4相乘!
如果定义二维数组b为b[3][17],内容和二维数组a相同。那么将很难找到直接使用二级指针来访问b的每一个字符串的方法了。