Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1433335
  • 博文数量: 1334
  • 博客积分: 645
  • 博客等级: 上士
  • 技术积分: 5762
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-25 16:56
文章分类

全部博文(1334)

文章存档

2014年(108)

2013年(1059)

2012年(169)

分类: C/C++

2013-06-05 14:45:56

二维数组指针和函数参数

前几天看到一段代码,是在函数参数中传递二维数组的例子,弄得很糊涂,最近看了一下书,总结一下。

1.指针和二维数组

我们这里先定义一个二维数组:int zippo[4][2];/*整数数组的数组*/

(1)zippo为二维数组的首地址,zippo[0]即是其第一个整型数组的首地址,我们知道,

如果定义一个 int myint[2],那么myint为其数组名,而且也是首地址,由此类推,那么我们可以知道zippo[0]是zippo[0][...]数组的首地址也是其第一个元素的地址;

(2)zippo为二维数组的首地址,我们也从一维的数组推,如果定义一个 int myint[2],那么myint+1为第二个元素的地址,很明显zippo+1也是其第二个元素的地址,不过这里第二个元素特殊一点,它的第二个元素的一个一维的整型数组,所以zippo+1指向的是第二个一维数组。

(3)关于二维数组的存储,如果是以行主序的存储的,那么其存储结构如下:

图(1)二维数组存储结构

先前我进入了一个误区,由于二维数组zippo是地址的地址,那么我想定义一个int **pint = zippo;那么就能够完整且能准确的使用二维数组,使用如下代码:

int zippo[4][2]={2,1,3,45,3};

代码段1int **pint = zippo;

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

{

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

{

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

}

printf("\n");

}

注:我在使用如下代码成功执行了。

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

{

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

{

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

}

printf("\n");

}

程序结果:

 2  1

 3  45

 3  0

 0  0

使用代码段1会在VC中会出现

很明显出现了段错误,由于数据在内存中的存储是是线性的存储的,如图(1)二维数组存储结构,为了我用使用 zippo[i][j] 可以呢??根据我的分析,应该是在声明一个二维数组的时候,编译的时候zippo成为二维数组的数组名使用的特殊用法,而我们声明int **pint = zippo;会让zippo二维数组名退化成一个指针,那么我们就只能当成一个指向整型的一个指针,我们看代码段2.

那么我换为代码段2,如下:

代码段2

     int **pint = zippo;

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

{

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

{

printf(" %d ", pint[i*2+j]);

}

printf("\n");

}

程序结果如下:

 2  1

 3  45

 3  0

 0  0

成功执行了,这里我们可能会奇怪,这里明明使用的是一个指针的指针,为什么能正常工作呢,我的理解是因为指针大小跟int型大小一致,这样可以正常使用,那我们来试试换成一个二维字符数组,

临时代码段1:char char_array[4][20] = {"a.txt", "b.txt"};  

char **pchar1 = char_array;

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

{

for (j=0;pchar1[i*20+j];j++)

{

printf(" %c ", pchar1[i*20+j]);

}

printf("\n");

}

编译通过,但是运行的时候结果不正确,结果如下:

 a  t

 ?       -  

 ? ? ? ? ? ? ? ? ?   ? ?   p  ?

 ?  ? ? x

不正确的原因就如刚刚我们分析的一样,因为 pchar1[i*20+j]等同于*(pchar1+i*20+j),那么取的是*(pchar1+i*20+j)的内容,但是定义的时候char **pchar1,所以*(pchar1+i*20+j)还是一个指针,所以不正确;

那我们换一下代码,试试我们刚刚的分析:

临时代码段2:char char_array[4][20] = {"a.txt", "b.txt"};  

char *pchar = char_array;

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

{

for (j=0;pchar[i*20+j];j++)

{

printf(" %c ", pchar[i*20+j]);

}

printf("\n");

}

运行结果:

 a  .  t  x  t

 b  .  t  x  t

正确运行,这样证明我们的猜想是正确的,我们仅仅使用一个指向字符的指针就可以成功遍历了,这是由于数据的存储模型决定的。

看到这里你可能会想到上面临时代码段1,不是由于是指向的一个指针出错的,pchar1[i*20+j]还是一个指针,那么我使用**(pchar1+i*20+j)是不是就正确了呢,那么你想错了,因为数据在内存中仅仅是用了一个地址而已,而不是将数据的地址存储在另一个地方再使用,就是我们使用的zippo的地址也就是其数据的首地址了,而不是指针的指针,所以**(pchar1+i*20+j)这样用会指向一个未知区域,这样做事禁止的。

那我们再换一次代码段3

代码段3

    int *pint = zippo;//这里换成了指向整型的指针

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

{

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

{

printf(" %d ", pint[i*2+j]);

}

printf("\n");

}

程序结果:

 2  1

 3  45

 3  0

 0  0

程序结果正确,原因我们已经在前面的分析中说明了,我们不嫌麻烦,在一次换代码段4

int *pint = *zippo;

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

{

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

{

printf(" %d ", pint[i*2+j]);

}

printf("\n");

}

程序结果:

 2  1

 3  45

 3  0

 0  0

程序也能运行正确,因为我们知道,其实zippo的值和*zippo,还有&zippo[0][0]zippo[0]的值都是一样的,所以这样使用是正确的,我们来在程序中看看这几个的值:

printf("zippo:%0x\n",zippo);

printf("zippo[0]:%0x\n",zippo[0]);

printf("*zippo:%0x\n",*zippo);

printf("&zippo[0][0]:%0x\n",&zippo[0][0]);

程序结果:

zippo:12ff0c

zippo[0]:12ff0c

*zippo:12ff0c

&zippo[0][0]:12ff0c

2.函数和二维数组

如果在要编写处理二维数组的函数,必须很好的理解指针与数组的关系,正确的声明数组的参数,我们就在这里详细的讲解一下。

若是我们要处理一个二维字符数组 char char_array[4][20] = {"a.txt", "b.txt"};使它输出,一种办法是使用在函数参数中使用一维数组,如下:

void Print(char *p)//void Print(char p[])也行

//在传递数组指针的时候,相当于做了char *p = char_array[i]的操作,p不会成为 //类似的数组名,而是退化成一个指向字符的指针

{

int i = 0;

while(p[i])

printf("%c",p[i++]);

printf("\n");

}

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

{

Print(char_array[i]);

}

当然这么编写肯定很麻烦,而且不这么规范,所以我当然就想到二维数组的指针传递了,如果要做二维数组的指针传递我们可以如下声明:

void Print1(char (*p)[20], int n)//n为二维数组中一维数组的个数

//void Print1(char p[][20], int n) 两种声明都行,{

int i;

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

    {  

        printf("array[%d]= %s\n",  i,*(p+i));  

    }

}

为什么要这么声明呢??因为传递二维数组指针的时候,编译器不知道传递进来的函数参数是什么特殊的指针,我们使用char (*p)[20]来做限定,限定传递的参数为一个第二维为20个元素的二维数组指针,而且p指针还不是数组名,所以不是常量,能进行自加等操作,

char (*p)[20]将(*p)加括号是因为间接运送符的优先级低于[]运算符,所以必须加上才能等同于char p[][20];下面我们再来看看几个可能让你不好理解的地方:

(1)void Print1(char p[][], int n) //错误的声明

我在《c primer plus》中知道,编译器会把数组符号转换成指针符号,例如ar[1]会被转换为ar+1,编译器这样转换需要知道ar所执行对象的大小,下面的声明:

void Print1(char ar[][20], int n) //合法的声明

就表示ar指向的是由20个char型构成的数组,所以ar+1表示“在这个地址上加20个字节的大小”,如果是空括号,那么编译器将不能正确处理。

(2)我们经过第一节的学习,我们可以知道可以使用一个指向字符的指针来处理多维数组,其具体使用如下:但是这样使用会处理一维和二维数组之间的转换,不到万不得已最好不用。

char char_array[4][20] = {"a.txt", "b.txt"}; 

Print2(char_array, 4, 20);

void Print2(char *p, int row, int col)

{

int i,j;

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

    {  

j = 0;

        while (p[i*col+j])

        {

printf(" %c ", p[i*col+j]);

j++;

        }

printf("\n");

    }

}

(3)void Print1(char **p, int n) //错误的声明 编译能通过

这种类型的声明跟第一种情况是一样的,编译器不能正确识别为二维数组,注意

char **p和char p[2][3]中的p在有些情况下能相互通用,比如我们直接在函数内部使用的使用,两者是通用的,但是如果作为函数的参数传递,那么是不可以的,一维数组也是一样的。

(4)void Print1(char *str[20], int n)//错误的声明 编译能通过

我们知道“*”的优先级低于“[]”,那么是20个一维字符数组,但是编译器同样不能断定每个一维数组的大小,那么就不能正常使用。

(5)通过上面的学习我们知道

 int (*pz) [2];//pz指向的一个包含两个int型的数组

 而 int * pz [2];//是定义了2个一维的int型数组,记住这两者是不一样的,其运算符的优先级关系上面已经说了,但是下面有一种特殊用法,我也一种不知道原因:

编程中,需要向某个函数传递一个字符串数组。测试代码如下:

#include   

#define M 2  

#define N 100  

  

void test(const char** pstr)  

{  

    int i = 0;  

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

    {  

10         printf("array[%d]= %s\n",  i,*(pstr+i));  

11     }  

12     return;  

13 }  

14   

15 int main()  

16 {  

17     char char_array[M][N] = {"a.txt""b.txt"};  

18       

19     test((const char**) char_array);  

20   

21     return 0;  

22 }  

当然这么编写会出现段错误,原因我在前面已经说明了,是因为数组指针在编译的时候已经退化为符号指针了,其pstr就是符号指针,

1. 传递给test的紧紧是二维字符数组的首地址;

2. test 参数中pstr的类型是char**, *(pstr+i)的类型是 char*,四字节的指针, 而**pstr的类型是char;

printf(" %d %d %d \n",sizeof(pstr),sizeof(*pstr),sizeof(**pstr));

但是

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

{

printf("%c",**(pstr+i));

}

会报错,虽然**(pstr+i)指向的是char类型,但是实际上指向的地址是实际上没有使用的,这样使用会造成错误。

如果我们把程序改为如下:

23 #include   

24 #define M 2  

25 #define N 100  

26   

27 void test( char** pstr)  

28 {  

29     int i = 0;  

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

31     {  

32         printf("array[%d]= %s\n",  i,*(pstr+i));  

33     }  

34     return;  

35 }  

36   

37 int main()  

38 {  

39     char * str_array[M] = {"a.txt", "b.txt"};  

40       

41     test(str_array);  

42   

43     return 0;  

44 }  

能正确运行,网上有一篇帖子说明了原因,但是我没怎么看懂,因为照理说编译器将不会正确识别pstr为二维数组指针的,但是不知道为啥??

参考:http://blog.csdn.net/fantasy666666/archive/2009/08/07/4422110.aspx

阅读(246) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~