A!die Software Studio Welcome to A!Die Software Studio

C 专家编程笔记(三) 数组和指针

by adie
2007-04-15 19:16:56
    数组的下标应该从 0 还是从 1 开始?我提议的妥协方案是 0.5,可惜他们未予认真考虑便一口回绝。
                                 ----StanKellBo


    在 x = y 中,x 代表一个地址,被称为左值。Y 代表的是地址中的内容,被称为右值。左值在编译时可知,右值在运行时才可知。数组名也代表一个地址,是一个左值,但它不能做为赋值的对象,于是 C 语言引入了可修改左值的概念,数组名是一个不可修改的左值。

    和普通变量不同的是,数组名做为右值时表示的并不是地址中的内容,而是地址本身!所以对于数组 array 有一个令人吃惊的性质: array == &array。数组的这个性质可以让你使用 array[i] 来访问数组元素,而不必使用 (&array)[i] 来访问数组元素,虽然后者才是更符合语法的写法。因为 C 语言对下标的解释是按照 addr + i * sizeof(T) 来解释的(根据加法的交换律也可以写成 i * sizeof(T) + addr,所以数组也可以按照 i[array] 形式来访问。在 C 中这除了让你的代码酷一点外没有其他用处,不过在 C++ 中可以借此来区分重载了 operator[] 的自定义类对象和原始指针操作),要进行下标操作的必须是一个地址,所以数组名即使作为右值也当作地址来解释的。这种特殊的方式让 C 语言数组在概念上变得异常复杂,而仅仅是为了访问数组元素的时候能有一个简单的语法。


    由于数组名做为右值解释的时候也是一个地址,所以表面看起来它和指针及其相似,更有人将数组名等同于常量指针(如果这中观念深入你的思想,你将会在很多细节处产生无穷的烦恼)。但指针作为普通变量符合普通变量的解释方法,左值时代表地址,右值是代表地址中的内容。指针并不具有象数组那样的 array == &array 属性。对指针取下标会先取变量地址中的内容,在把这个内容当作地址来进行偏移操作。下图展示了 int a[5]; 和 int* p = new int[5] 的内存布局区别。



    作为函数定义的形式参数,数组和指针是一样的。
    C 的函数参数是按值传递的,而 C 是不允许对数组进行整体操作的,自然你也不能把整个数组作为参数来传递。实际上在函数参数中的数组是被当作指针处理的,借助于 C 对数组和指针下标访问的相似性,使得这些看起来好像通过参数传递了数组一样。这是 C 为了语法的一点简单而导致概念混乱的又一例子。这也导致了对数组在经过函数参数传递后再取 sizeof 得到的会是指针大小,而且再对其取地址已经不具有 array == &array 属性了。

    规则3. 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针(具体释义见 ANSI C 标准第 6.7.1 节)(仔细琢磨琢磨,这句话是有问题的。亦或是翻译问题?)

    他们向函数传递数组前面一个位置的地址 (a[-1]), 这样就可以使数组的下标从 1 到 N, 而不使从 0 到 N - 1.   …   不幸的是,这个做法完全为标准所不容, 可能引起未定义行为,所以你千万别告诉别人是我告诉了你这个办法.
    要取得 Fortran 程序员需要的效果其实非常简单:只要在数组的声明中(显然这里应该说定义)让它的长度比所需的多 1. 这样数组的下标范围就是 0 到 N,然后只使用 1 到 N 就是了.不必疑惑,不比惊诧,就是这么简单.


    Ada 中数组的数组和多维数组是不一样的.
    Pascal 中数组的数组和多维数组是可以互换的.
    C 语言中只有数组的数组,但 C 语言把它叫做多维数组.

    C 语言的多维数组中,最右边的下标是最先变化的,这个约定被称为"行主序".绝大部分语言都采用行主序,但 Fortran 是列主序的. C 语言的行主序对于存储多个字符串时有优势(如果你的字符串是用字符数组来表示的话).
    C 语言中的多维数组就是数组的数组,所以这个行主序的特性其实是由 [] 操作符是左结合,并且从符合左到右的顺序决定的.
    多维数组是数组的数组,绝对不是指针的指针(我周围就有人是这么认为的);也不是指针的数组(main 的 argv 参数是一个指针的数组);但在某些情况下可以认为是数组的指针.可能有些乱,可以用下表来描述一下:

        类型              在某些情况下认为是
       数组              指针
       数组的数组        数组的指针
       指针的数组        指针的指针
       数组的数组的数组  数组的数组的指针
       指针的数组的数组  指针的数组的指针

    二维数组(数组的数组)和指针的指针(指针的数组, main 的 argv 参数)具有相同的访问方法(我想这应该是认为二维数组和指针的指针相同的主要原因): 
     void  f1(char array[3][3])
     {
        … array[0][0];  通过两个下标来访问
     }

     void  main(int argc, char** argv) 也可写成 char* argv[]
     {
        … argv[0][0];  和二维数组的访问方法一样
     }

    但它们的内存布局是不一样的.下图来描述多维数组和指针的指针之间的区别:


▲评论

X 正在回复:
姓 名: 留下更多信息
性 别:
邮 件:
主 页:
Q Q:
来 自:
职 业:
评 论:


Valid HTML 4.01 Strict Valid CSS!
Copyleft.A!die Software Studio.ADSS
Power by webmaster@adintr.com