一、一维数组
1.数组名
数组名的值是一个指针常量,因此不能使用赋值符复制数组。(常量不许为左值,左值应该是一个相当于地址的量)
两种例外情形:
数组名作sizeof操作符的操作数时。(此时结果为整个数组的长度(以字节为单位))
数组名作&(取地址)的操作数时。(此时结果为一个指向数组的指针)
eg: int a[10];
int (*p)[10] = &a; //p为指向数组的指针,&a为指向数组的指针
2.下标引用
除优先级外,下标引用和间接访问完全相同。(下标高于间接访问)
eg: (下标引用) array[subscript]
(间接访问) *( array + (subscript))
特殊写法 subscript[array]
以上三者均等价
c下标检查所涉及的开销较多,所以一般不对下标做检查,通常程序员需要自己做检查。
3.指针与下标
可读性方面,下标更优(尤其多维数组),效率方面,指针访问的效率可能更高。
关于指针效率:
当根据某个固定数目的增量在数组中移动时,使用指针变量将比使用下标产生更高的效率代码。
声明为寄存器的变量的指针通常比位于静态内存和堆栈中的指针效率更高。(不一定要做声明,编译器可能会自行优化)
程序维护是软件产品的主要成本所在。
4.作为函数参数的数组名
所有传递给函数的参数都是通过传值方式进行的。当传递的是数组名(指针)时,传递的是它的一份拷贝,我们对拷贝的修改并不会改变实参本身,但是我们通过间接访问可以达到对其所指向的内容做修改(相当于副作用)。这样表现出来的效果如同传址调用。
5.声明数组参数
调用函数时,实际传递的是一个指针,函数的形参实际是一个指针。
eg:下列函数原型等价
int strlen( char *string )
int strlen(char string[])
注:数组形参可与任何长度的数组匹配,若需传递数组长度则必须将长度作为一个显示的参数传递给相应函数。
6.初始化
静态初始化,数组元素在缺省情形全初始化为0;自动初始化缺省情形数组元素是未初始化的。
不完整的初始化,后面元素自动补零;初始化时可不给出数组长度,由编译器自动计算。
字符数组的初始化,可以用字符串常量赋值,此时字符串常量自动解释为初始化列表。
二、多维数组
1.存储顺序
多维数组的元素的存储顺序按照最右边的下标率先变化的原则,称为行主序。
a [0] [0] | a [0] [1] | a [0] [2] | a [1] [0] | a [1] [1] | a [1] [2] |
内存中数据单元的实际存储方式
二维数组名是指向数组的指针,定义如下:
int matrix [3] [5];
int (*p) [5] = matrix; //p是指向含有五个int元素的数组的指针
matrix + 1 //指向包含5个整型元素的数组的指针,指向matrix的第二行
*(matrix + 1) //指向整型的指针
*(matrix + 1)+ 5 //指向整型的指针
*( *(matrix + 1)+ 5 ) //整型
//pi定义为整型指针
int *pi = &matrix[0][0];
int *pi = matrix[0];
2.多维数组作为函数参数
多维数组作为函数参数时必须声明除第一维以外的其他维长度(下标需用于计算长度)
eg:
void fun( int (*mat) [10] ) ;
void fun( int mat [] [10] ) ;
void fun( int **mat ); //error,指向整形指针的指针和指向整形数组的指针不同
3.初始化
只有第一维才能根据初始化列表缺省地提供。
三、指针数组
eg:
int *api [10]; // api是一个包含10个元素的数组,元素为整型指针 (指针数组)
int (*api) [10]; // api是一个指针,指向包含10个元素的整型数组 (指向数组的指针)