08 数组

8.1 一维数组

数组名在表达式中被当做指针常量

数组不是指针,数组有元素数量等属性。编译器通过数组名来记住这些属性。只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。

但在表达式中使用,还是有两中特殊情况

  • sizeof(数组名):返回的是整个数组的长度,而不是指向数组的指针的长度。

  • &:返回一个指向数组的指针,而不是指向指针常量的指针。

int a[10];
int b[10];
int *c;
c = &a[0]; // &:返回一个指向数组的指针,而不是指向指针常量的指针。
c = a; // 和上面的语句完全等价。
b = a; // 非法,b是指针常量
a = c; // 非法,a是指针常量,不能被修改。

声明数组和指针

在声明数组的时候,编译器根据指明的元素数量为数组保留内存空间。另外再创建数组名,其是一个常量,指向这段空间的起始位置。

在声明指针的时候,编译器只为指针自身分配空间。

数组名和指针的效率问题

根据固定数目的增量在一个数组中移动时,指针更有效率。下面第二个例子的效率更高,因为第一个例子中array[a]每次都要用a乘以整型的长度,会多花费一些时间。

int array[10], a;
for (a = 0; a < 10; a += 1)
array[a] = 0;
int array[10], *ap;
for (ap = array; ap < array + 10; ap++)
*ap = 0;

如果只用一次,则都会使用乘法,效率是一样的。如下面两个

array[a] = 0;
*(array + a) = 0;

作为函数参数的数组名

数组名作为参数时,会转化成指针。传递给函数的是指向数组起始位置的指针的拷贝,所以在函数内部可以自由的操作指针。

在声明数组参数时,也写成指针

int strlen(char *string);
int strlen(char string[]); // 这种写法是考虑为照顾新手才有的,实际上上面的写法才最准确,因数这里的参数就只指针。

字符数组的初始化

下面一二两种写法等价,注意第二部不是字符串常量,只是一种便捷写法。第三行才是真正的字符串常量。

char message[] = {'H', 'e', 'l', 'l', 'o', 0};
char message[] = "Hello"; // 不是字符串常量,会给数组分配6字符的空间
char *message2 = "Hello"; // message2是指针,指向字符串常量的地址。

8.2 多维数组

c中,多维数组的元素的存储顺序按照最右边的下标率先变化,称为行主序(row major order)。

int matrix[6][10];
imt *mp;
...
mp = &matrix[3][8];
printf("First value is %d\n", *mp); // 打印[3][8]
printf("Second value is %d\n", *++mp); // 打印[3][9]
printf("Third value is %d\n", *++mp); // 打印[4][0]

多维数组的下标和间接访问

下面三个是等价的。

matrix[3][5];
*(*(matrix + 3) + 5);
*(matrix[3] + 5);

指向数组的指针

下面第二个是错的,因为mp是指向整型的指针,而matrix是指向十个整型的数组的指针。

int vector[10]; *vp = vector;
int matrix[3][10]; *mp = matrix;

正确的写法应该是

int (*p)[10] = matrix; // 首先p是指针,然后p指向的内容是一个10个元素的数组

作为函数参数的多维数组

函数参数会把数组转化为指针。

int matrix[3][10];
...
func2(matrix); // 这里是调用
void func2(int (*mat)[10]); // 两种写法都正确,但是第一种更准确
void func2(int mat[][10]);

一个错误写法示例

void func2(int **mat); // 我们需要的是指向10个元素的整型数组的指针,但是这里是指向指针的指针。

8.3 指针数组

下标引用的优先级高于间接访问,所以下面的表达式首先算下标引用,即api是个数组。其次数组中元素的类型是int*,即这是一个存放指针的数组。注意与指向数组的指针的区别。

int *api[10];

指针数组一个最常用的例子是用来存放多个字符串。

char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
NULL,
};

首先keyword是一个数组,然后每个元素的类型是char const *,即指向字符串首地址的指针。我们把最后一个元素存放NULL,这样便可以方便的在for中使用。

for (kwp = keyword; *kwp != NULL; kwp++) {...}