数组

数组

一维数组

       数组名的值是一个指针常量,也就是数组第一个元素的地址。

       注意数组名为指针常量,而不是指针变量,不能改变。

下标引用

       对于已知的数组长度,编译器将不检查它的下标,因为涉及到位置和长度方面的信息,将会影响程序的执行效率。

       2[array]在有些编译器中的含义为*(array + 2)

指针与下标

       下标绝不会比指针更有效率,但指针有时会比下标更有效率

       当然,指针的高效率前提是被正确地使用。

指针的效率

clip_image002

clip_image004

       从上面的一个数组复制到另一个数组的总结为:

l  当你根据某个固定数目的增量在一个数组中移动时,使用指针变量将比使用下标产生更高的代码,当这个增量是1并且机器具有地址自动增量模型时,这点表现更为突出;

l  声明为寄存器变量的指针通常比位于静态内存和堆栈中的指针效率更高(具体提高的幅度取决于你所使用的机器);

l  如果你可以通过测试一些已经初始化并经过调整的内容来判断循环是否应该终止,那么你就不需要使用一个单独的计数器;

对于使用指针或者数组的判断:

Ø  程序的维护是软件产品的主要成本所在,所以要多考虑一下维护方便;

Ø  有些机器在设计时使用了特殊的指令,用于执行数组下标的操作,目的就是为了使这种极为常用的操作更加快速,在这种机器上,编译器将使用这些特殊的指令来实现下标表达式,但编译器并不一定会用这些指令来实现指针表达式,所以在这种机器上,下标表达式可能会比指针更有效率;

Ø  关于使用哪种方式,最主要的还是要先确认程序中到底是那段代码占用了绝大部分的运行时间,然后把精力用在这些代码上,努力改进它们。

数组和指针

 

 

 

 

 

int a[5];       

      

       会直接分配内存空间。

 

 

?

int *b;       

 

并未被初始化为指向任何现有的内存空间,甚至根本不会被初始化。

作为函数参数的数组名

void strcpy(char *buffer, char const *string);

       该函数中const的优点:

Ø  这是一个良好的文档习惯,通过函数原型就可以发现该数据不会被修改,而不用阅读完整的函数定义;

Ø  编译器可以捕捉到任何试图修改该数据的意外错误;

Ø  这类声明允许向函数传递const参数。

声明数组参数

int strlen(char *string);

int strlen(char string[]);

这两个其实含义是一样的,调用函数时实际传递的是一个指针,所以函数的形参实际上是个指针。但是为了使程序员新手更容易上手一些,编译器也接受数组形式的函数形参。

       但是两个定义中,指针的声明更加准确一些

字符数组的初始化

       当用于初始化一个字符数组时,字符串为一个初始化列表,在其他任何地方,表示一个字符串常量

指向数组的指针

       int matrix[3][10];

       int (*p)[10] = matrix;–指向数组的指针

 

作为函数参数的多维数组

void func(int (*mat)[10]);         指向整型数组的指针

void func(int **mat);             指向整型指针的指针

初始化

       多维数组的初始化最好加上花括号,比如:

int a[2][3][4] =

{

{

{},

{1,2,3,4},

{}

},

{

{},

{1,1,1,1},

{}

}

};

原因如下;

l  利于显示数组的结构;

l  对于不完整的初始化列表,相当有用,比如上面的只初始化其中的部分数据,如果不加花括号,要好好数对位置才行;

数组长度自动计算

       要求是只有第一维可以缺省提供,其余的几维必须显示写出。但其实也可以缺省,主要是由于必须要把子维的数组要达到最长要求才行(即子初始化列表中必须有一个完整的形式),否则还是不对。

总结

       在绝大多数表达式中,数组名的值是指向数组第一个元素的指针。这个规则只有两个例外

l  sizeof返回整个数组占用的字节而不是一个指针所占用的字节;

l  单目操作符&返回一个指向数组的指针,而不是一个指向数组第一个元素的指针的指针。

 

指针和数组并不相等:

l  当我们声明一个数组时,它同时也分配了一些内存空间,用于容纳数组元素;

l  当我们声明一个指针时,它只分配了用于容纳指针本身的空间。

 

只要有可能,函数的指针形参都应该声明为const