C 语言数组指针和指针数组
C 语言数组指针和指针数组
指向数组的指针被称为数组指针 (array pointer)。
具有指针类型元素的数组被称为指针数组 (pointer array)。
1. 数组指针
要声明指向数组类型的指针,必须使用括号
int (*arrPtr)[10] = NULL; // 一个指针,它指向一个有 10 个 int 元素的数组。
在该例中,指向有 10 个 int 元素的数组的指针会被初始化为 NULL。如果把合适数组的地址分配给它,那么表达式 *arrPtr
会获得数组,并且 (*arrPtr)[i]
会获得索引值为 i
的数组元素。根据下标运算符的规则,表达式 (*arrPtr)[i]
等同于 *((*arrPtr)+i)
。**arrPtr
获得数组的第一个元素,其索引值为 0。
如果没有括号,则声明 int *arrPtr[10];
表示 arrPtr 是一个具有 10 个 int 类型指针的数组。
int *p[9];
int (*p)[9];
数组指针为通过指针引用数组,p
先和 *
结合,说明了 p
是一个指针变量,指向一个大小为 9
的数组。int (*p2)[10];
为一个数组指针。int *p1[10];
为一个大小为 9 且存放整型指针的数组。
数组指针只是一个指针变量,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
指针数组 (array of pointers):首先是一个数组,数组中的每一个元素都是指针。数组占多少个字节由数组本身决定。简称储存指针的数组。
数组指针 (a pointer to an array):首先是一个指针,用指针指向一个数组。在 32 位系统下是占 4 个字节,至于指向的数组占多少字节。简称指向数组的指针。
2. 指针数组
指针数组 (元素为指针类型的数组) 常常作为二维数组的一种便捷替代方式。一般情况下,这种数组中的指针会指向动态分配的内存区域。
声明指针数组:
int *api[10];
下标引用的优先级高于间接访问。
我们假定它是一个表达式,并对它进行求值。在这个表达式中,首先执行下标引用。api 是数组 (包含的元素个数为 10)。在取得一个数组元素之后,随即执行的是间接访问操作。这个表达式不再有其他操作符,所以它的结果是一个整型值。对数组的某个元素执行间接访问操作后,我们得到一个整型值,所以 api 肯定是个数组,它的元素类型是指向整型的指针。
指针表达式可能比下标表达式效率更高,但下标表达式绝不可能比指针表达式效率更高。但是,以牺牲程序的可维护性为代价获得程序的运行时效率的提高可不是个好主意。
指针和数组并不相等。数组的属性和指针的属性大相径庭。当我们声明一个数组时,它同时也分配了一些内存空间,用于容纳数组元素。但是,当我们声明一个指针时,它只分配了用于容纳指针本身的空间。
当数组名作为函数参数传递时,实际传递给函数的是一个指向数组第 1 个元素的指针。函数所接收到的参数实际上是原参数的一份拷贝,所以函数可以对其进行操纵而不会影响实际的参数。但是,对指针参数执行间接访问操作允许函数修改原先的数组元素。数组形参既可以声明为数组,也可以声明为指针。这两种声明形式只有当它们作为函数的形参时才是相等的。
数组也可以用初始值列表进行初始化,初始值列表就是由一对花括号包围的一组值。如果初始值列表包含的值的个数少于数组元素的个数,数组最后几个元素就用缺省值进行初始化。如果一个被初始化的数组的长度在声明中未给出,编译器将使这个数组的长度设置为刚好能容纳初始值列表中所有值的长度。字符数组也可以用一种很像字符串常量的快速方法进行初始化。
静态变量 (包括数组) 在程序载入到内存时得到初始值。自动变量 (包括数组) 每次当执行流进入它们声明所在的代码块时都要使用隐式的赋值语句重新进行初始化。
多维数组实际上是一维数组的一种特型,就是它的每个元素本身也是一个数组。多维数组中的元素根据行主序进行存储,也就是最右边的下标率先变化。多维数组名的值是一个指向它第 1 个元素的指针,也就是一个指向数组的指针。对该指针进行运算将根据它所指向数组的长度对操作数进行调整。多维数组的下标引用也是指针表达式。当一个多维数组名作为参数传递给一个函数时,它所对应的函数形参的声明中必须显式指明第 2 维 (和接下去所有维) 的长度。由于多维数组实际上是复杂元素的一维数组,一个多维数组的初始化列表就包含了这些复杂元素的值。这些值的每一个都可能包含嵌套的初始值列表,由数组各维的长度决定。如果多维数组的初始化列表是完整的,它的内层花括号可以省略。在多维数组的初始值列表中,只有第 1 维的长度会被自动计算出来。
我们还可以创建指针数组。字符串的列表可以以矩阵的形式存储,也可以以指向字符串常量的指针数组形式存储。在矩阵中,每行必须与最长字符串的长度一样长,但它不需要任何指针。指针数组本身要占用空间,但每个指针所指向的字符串所占用的内存空间就是字符串本身的长度。
在绝大多数表达式中,数组名的值是指向数组第 1 个元素的指针。这个规则只有两个例外。sizeof
返回整个数组所占用的字节而不是一个指针所占用的字节。单目操作符 &
返回一个指向数组的指针,而不是一个指向数组第 1 个元素的指针的指针。
数组指针用于指向一个数组,数组名是数组首元素的起始地址但并不是数组的起始地址。通过取地址符 &
作用于数组名可以得到数组的起始地址。
- 一开始就编写良好的代码显然比依赖编译器来修正劣质代码更好。
- 源代码的可读性几乎总是比程序的运行时效率更为重要。
- 只要有可能,函数的指针形参都应该声明为 const。
- 在有些环境中,使用 register 关键宇提高程序的运行时效率。
- 在多维数组的初始值列表中使用完整的多层花括号能提高可读性。