二维数组指针和函数参数

http://blog.chinaunix.net/uid-25324849-id-336253.html

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

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 <stdio.h>  

#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 <stdio.h>  

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









  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二维数组函数指针参数是指将二维数组作为参数传递给函数,并且该函数的参数是一个函数指针。函数指针是指向函数的指针变量,可以用来调用特定的函数。 以下是一个示例代码,演示了如何传递二维数组和函数指针参数: ```c #include <stdio.h> // 函数指针类型定义 typedef void (*FuncPtr)(int, int); // 函数使用二维数组和函数指针作为参数 void processArray(int arr[][3], int rows, int cols, FuncPtr func) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { // 调用函数指针所指向的函数 func(arr[i][j], i); } } } // 示例函数,打印数组元素的值和索引 void printElement(int value, int index) { printf("Element [%d][%d]: %d\n", index / 3, index % 3, value); } int main() { int arr[2][3] = {{1, 2, 3}, {4, 5, 6}}; // 调用 processArray 函数,并传递二维数组和函数指针参数 processArray(arr, 2, 3, printElement); return 0; } ``` 在上述代码中,`processArray` 函数接受一个二维数组 `arr`,以及行数和列数作为参数,同时还接受一个函数指针 `func`。在 `processArray` 函数中,使用嵌套的循环遍历二维数组,并通过调用函数指针所指向的函数来处理数组元素。 在 `main` 函数中,定义了一个二维数组 `arr`,然后调用 `processArray` 函数,并将 `arr`、行数、列数以及 `printElement` 函数的函数指针作为参数传递进去。`printElement` 函数用于打印数组元素的值和索引。 运行上述代码,会输出如下结果: ``` Element [0][0]: 1 Element [0][1]: 2 Element [0][2]: 3 Element [1][0]: 4 Element [1][1]: 5 Element [1][2]: 6 ``` 这样,我们就成功地将二维数组和函数指针作为参数传递给了函数,并在函数内部进行了处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值