4.指针和数组
一种常见的错误观点是“数组和指针是完全可以互换的”。尽管数组名字有时候可以当作指针来用,但数组的名字不是指针。数组表示法也可以和指针一起使用,但两者明显不同,也不一定能互换。尽管数组使用自身的名字可以返回数组地址,但名字本身不能作为赋值操作的目标。
4.1.1 一维数组
一维数组是线性结构,用一个索引访问成员。数组索引从0开始,到声明的长度减1结束。
对数组做sizeof
操作会得到为该数组分配的字节数,要知道元素的数量,只需将数组长度除以元素长度。
4.1.2 二维数组
二维数组使用行和列来标识数组元素,这类数组需要映射为内存中的一维地址空间。在C中这是通过行-列顺序实现的。先将数组的第一行放进内存,接着是第二行、第三行,直到最后一行。
可以将二维数组当作数组的数组,也就是说,可以只用一个下标访问数组,得到的是对应行的指针。下面片段代码会打印每一行的地址和长度:
int matrix[2][3] = { {1,2,3},{4,5,6} }; for (int i = 0; i < 2; i++) { printf("&matrix[%d]:%p sizeof(matrix[%d]):%d ", i, &matrix[i], i, sizeof(matrix[i])); } &matrix[0]:100 sizeof(matrix[0]):12 &matrix[1]:112 sizeof(matrix[1]):12 //假设数组位于地址100处
4.2指针表示法和数组
单独使用数组名字时会返回数组地址。可以把地址赋给指针,如下所示:
int vector[5] = {1,2,3,4,5};
int *pv = vector;
pv变量是指向数组第一个元素而不是指向数组本身的指针。给pv赋值是把数组的第一个元素的地址赋给pv。数组的首地址,也就是第一个字符的地址
我们可以只用数组名字,也可以对数组的第一个元素用取地址操作符,这些写法是等价的,都会返回vector
的地址。
printf("%p
",vector);
printf("%p
",&vector[0]);
有时候也会使用&vector
这个表达式获取数组地址,不同于其它表示法,这么做返回的是整个数组的指针,其他两种方法得到是整数指针。
我们可以把数组下标用在指针上,实际上pv[i]这种表示法等价于: *(pv +i)
pv指针包含一个内存块的地址,方括号表示法会取出pv中包含的地址,用指针算术运算把索引i加上,然后解引新地址返回其内容。下面两个语句是等价的:
*(pv + i) == *(vector + i)
pv + i实现“基址 + 位移”的运算,其值恰为数组 vector 第i个元素的地址,即&vector[i]。
由于数组元素的下标在内部实现是统一按“基址 + 位移”的方式处理的,这样,一个指向数组的指针也能够以数组名的形式出现 pv[i]等价于 vecotr[i]
vector[i]
生成的代码和*(vector+i)
生成的不一样,vector[i]
表示法生成的机器码从位置vector
开始,移动i
个位置,取出内容。而*(vector+i)
表示法,生成的机器码则是从vector
开始,在地址上增加i
,然后取出这个地址中的内容。尽管结果是一样的,生成的机器码却不一样,对于大部分人来说,这种差别几乎无足轻重。
sizeof
操作符对数组和同一个数组的指针操作也是不同的。对vector
调用sizeof
操作符会返回20,就是这个数组分配的字节数。对pv
调用sizeof
操作符会返回4,就是指针的长度。
pv
是一个坐值,左值表示赋值操作符左边的符号。左值必须能修改。像vector
这样的数组名字不是左值,它不能被修改。
4.3 使用malloc创建一维数组
如果从堆上分配内存并把地址赋给一个指针,那就肯定可以对指针使用数组下标并把这块内存当成一个数组。
int *pv = (int *)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++) {
pv[i] = i + 1;//*(pv + i) = i + 1 指针表示法
}
用malloc创建的一维数组也可以使用数组表示法,但是用完之后要记得释放内存。
4.4 用realloc调整数组长度
用realloc函数通过一个定长增量来分配额外空间。
char *getLine(void) { const size_t sizeIncrement = 10; //缓冲区的初始大小以及需要增大时的增量 char *buffer = malloc(sizeIncrement); //指向读入字符的指针 char *currentPosition = buffer; //指向缓冲区中下一个空白位置的指针 size_t maximumLength = sizeIncrement; //可以安全地存入缓冲区的最大字符数 size_t length = 0; //读入的字符数 int character; //上次读入的字符数 if (currentPosition == NULL) return NULL; while(1){ character = fgetc(stdin); //从标准输入每次读取一个字符 if (character == ' ') break; //如果是回车符,循环退出 if (++length >= maximumLength) //判断有没有超出缓冲区大小 { char *newBuffer = realloc(buffer, maximumLength += sizeIncrement); if (newBuffer == NULL) { //如果无法分配内存 free(buffer);//释放已分配内存 强制函数返回NULL return NULL; } //新分配的地址可能在原地址也有可能在其它位置 currentPosition = newBuffer + (currentPosition - buffer); buffer = newBuffer; } *currentPosition++ = character; //没有超出字符添加到缓冲区中 } *currentPosition = '