简析指针与多维数组

2013-06-17 17:16

简析指针与多维数组

by

at 2013-06-17 09:16:35

original http://tonybai.com/2013/03/28/pointer-and-multi-dimension-array-in-c/?utm_source=rss&utm_medium=rss&utm_campaign=pointer-and-multi-dimension-array-in-c

上一篇文章中对多级指针做了简要分析,其实只有当指针与多维数组以及函数联合在一起使用时,麻烦才算真正到来。

零、数组与数组名

C语言中的数组的一般声明形式如下:

T arr_name[n]; /* T为类型,n为数组元素个数 */

内存布局角度来说,数组T arr_name[n]就是内存中连续的内存单元,每个内存单元的长度为sizeof(T),数组的起始内存单元地址为arr_name所在的内存地址, 同时也是数组第一个元素arr_name[0]的内存地址。

C语言数组的数组名(arr_name)有这样的特点:arr_name = &arr_name = *arr_name = 数组起始地址。见下面例子:

char a[5];

printf("a = %p\n", a);
printf("&a = %p\n", &a);
printf("*a = %p\n", *a);

输出结果:

a = 0xbfb146c0
&a = 0xbfb146c0
*a = 0xbfb146c0

C语言数组与指针有着紧密的联系。数组名本身的值就是数组的起始地址,有了地址,就有了指针存在的理由了。

1) 数组名可以被当作指针来用

    char a[5] = {1, 2, 3, 4, 5};
    printf("%d, %d, %d\n", *a, *(a+1), *(a+2)); // 输出1, 2, 3

   
    这种用法下,数组名相当于指向数组首地址的char*指针变量。

2) 数组名可以作为地址被赋值给兼容类型的指针变量
   
    char a[5] = {1, 2, 3, 4, 5};
    char *p = a;
    printf("%d, %d, %d\n", *p, *(p+1), *(p+2)); //输出1, 2, 3

3) 数组名不可以被当作指针变量来赋值

    char a[5] = {1, 2, 3, 4, 5};
    char b[5] = {6, 7, 8, 9, 0};

    a = b; //编译器提示错误:将‘char *’赋值给‘char[5]’时类型不兼容

    数组名与指针变量不同:指针变量有单独的存储空间,其存储空间内存储的是指向的内存单元的地址,但数组名只是个"代号"而已,其没有单独的存储空间,其所 在内存地址中存储的是数组第一个元素的元素值,而不是一个地址。或者说数组名代表的是一个值类型,char a[5]中的a可理解为是一个char[5]的值类型变量。将一个数组指针变量值赋值给一个值变量显然是不合逻辑的,也是非法的。

4) 考虑到效率,数组无法被按值传递给函数
   
    虽然数组名可以理解为一个值类型变量,但将数组名传递给函数时,传递的不是数组的全部,而只是数组的首地址,这显然是有效率方面考虑的。如果是传递数组的 全部,那碰到大数组时,这个mem copy的效率显然是不可接受的。但通过这个首地址,函数内部也是可以访问和修改数组中的所有元素的。
   
5) 函数形参中的数组变量将被转化为兼容类型指针变量对待

正如4)中所言,数组是以传址方式传入函数的。对于以数组变量作为形参的函数来说,在函数内部引用该参数时,会自动将该参数视为数组类型兼容的指 针变量,比如:
    char a[5] = {1, 2, 3, 4, 5};

    void foo(char a[5]) {
        printf("sizeof(a) = %d\n", sizeof(a));
    }

    这是一个经典的C语言“陷阱”。foo形参中变量a已经转化为一个char*类型指针了。对该指针变量进行sizeof操作,所得的 size仅是一个指针的长度(在32bit编译下是4),而不是a数组的长度(4 * 5)。

一、多维数组的理解

C语言中管数组的数组(的数组的…)称为多维数组,虽然高于二维的多维数组并不经常使用和遇见。

T multi_arr_name[i][j][k];

多维数组也是数组,根据数组的理解,多维数组也是内存中连续分配的内存单元,只是这些物理分配的内存单元被从逻辑上看成是“行”、“列”以及各种 维度罢了。《C专家编程》中有一种理解方法:将数组看成是一种向量,也就是某种对象的一维数组;当其元素为其他数组时,这个向量也就是我们所说的 多维数组。

我们来结合例子理解一下多维数组,从低维到高维度逐步理解:

1) 一维数组

char a[2];
这是一个向量,拥有两个元素,向量中的元素类型为char。可以理解为:

char a[2]; <=> (char) a[2];

2) 二维数组

char a[2][3];
这是一个向量,拥有两个元素,向量中的元素类型为char[3]。可以理解为:

char a[2][3]; <=> (char[3]) a[2];

3) 三维数组

char a[2][3][5];
这是一个向量,拥有两个元素,向量中的元素类型为char[3][5]。可以理解为:

char a[2][3][5]; <=> (char[3][5]) a[2];

4) N维数组

char a[i][j][k]…[z];
这是一个向量,拥有i个元素,向量中的元素类型为char[j][k]…[z]。可以理解为:

char a[i][j][k]…[z]; <=> (char [j][k]…[z]) a[i];

二、与数组类型兼容的指针类型

假设有下面这样一个数组:

char a[2][3];

我要声明一个可以指向该数组的指针变量,这个声明该如何书写呢?是 char *p[3]还是char (*p)[3]?按照上面对多维数组的理解:

char a[2][3];