数组显然不是指针。可以肯定的是,两个 lvalues 似乎都包含(一维)虚拟内存中某个位置的(一维)坐标。但是考虑这个例子。
#include <stdlib.h>
#include <stdio.h>
int main(){
char buffer0[4096];
char* buffer1 = malloc(4096);
printf("lvalue %16p sizeof %lu\n", (void *) buffer0, sizeof(buffer0));
printf("lvalue %16p sizeof %lu\n", (void *) buffer1, sizeof(buffer1));
// Example output: lvalue 0x7ffcb70e8620 sizeof 4096
// Example output: lvalue 0x7a4420 sizeof 8
}
想到的实际差异是:
- 数组知道它们有多大(以字节为单位)(并且,通过扩展,它们知道它们有多少个元素);指针不知道(但
malloc()
必须 知道指针有多大,才能知道仅给定指针的free()
有多少...!) - 数组是“垃圾收集器”(不需要
free()
它们);必须手动释放指针(如果它们拥有大量内存,即通过malloc()
) - 数组“活”在堆栈中(高虚拟内存地址,至少在我的平台上);指针“活”在堆中(低虚拟内存地址)
- 数组在传递给函数时衰减为指针
- 数组不能调整大小;指针可以
总的来说,数组似乎比指针更聪明(但通用性较低)(它们知道它们有多大,它们有多少元素,并且它们具有自动内存管理)。
问题
- 数组如何“知道”它们有多大?这是如何实现的?
- 一般来说,数组在C语言中是如何实现的? (编译器会这样做,还是内核会这样做?
最佳答案
How do arrays "know" how big they are? How is this implemented?
数组不知道它们有多大——没有与数组关联的元数据来指示大小(或类型,或其他任何内容)。 在翻译过程中,编译器知道数组有多大,并且在那个时候处理任何依赖于该知识的知识(指针算术、sizeof
操作等)。一旦生成了机器代码,数组就只是一些愚蠢的内存块——在运行时无法通过查看数组对象本身来确定数组有多大(可变修改类型除外,如变量-length 数组,sizeof
操作是在翻译期间计算的,而不是运行时)。
In general, how are arrays implemented in the C language? (Does the compiler do this, or does the kernel?
数组只不过是同一类型对象的连续序列。对于声明
T arr[N]; // for any type T
你得到
+---+
arr: | | arr[0]
+---+
| | arr[1]
+---+
| | arr[2]
+---+
...
+---+
| | arr[N-1]
+---+
没有独立于数组元素本身的 arr
对象,也没有为大小、起始地址、类型或其他任何内容预留任何元数据。
下标操作arr[i]
被定义为*(arr + i)
- 给定数组的起始地址,偏移量i
个元素(不是字节!)并解引用结果。
你说数组不是指针是正确的 - 但是,除非它是 sizeof
或一元 &
运算符的操作数,或者是用于初始化 a 的字符串文字声明中的字符数组,数组类型的表达式将被转换(“衰减”)为指针类型的表达式,表达式的值将是数组第一个元素的地址(同样,这一切都是在翻译期间完成的,而不是在运行时)。
因此,当您编写类似x = arr[i];
的代码时,编译器会将表达式arr
转换为指针值,因此下标操作有效。
相比之下,当您编写 ap = &arr;
时,编译器不会将 arr
转换为指针类型。结果仍然与第一个元素的地址相同,但是类型不同——不是T *
,类型是T (*)[ N]
,或“指向 T
的 N 元素数组的指针”。
关于c - C中的数组 "implemented"如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50450578/