如何在C中"实现"数组?

éta*_*ogy 3 c arrays gcc pointers

一个阵列是特别一个指针.可以肯定的是,两个左值似乎都包含(1维)虚拟内存中某个位置的(1维)坐标.但请考虑这个例子.

#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
}
Run Code Online (Sandbox Code Playgroud)

想到的实际差异是:

  1. 数组知道它们有多大(以字节为单位)(并且,通过扩展,它们知道它们有多少元素); 指针不会(但malloc() 必须知道指针有多大,知道free()只给指针多少......!)
  2. 数组是"垃圾收集"(不需要free()它们); 指针必须手动释放(如果它们拥有非常重要的内存量,即通过malloc())
  3. 阵列"活"在堆栈中(高虚拟内存地址,至少在我的平台上); 指针在堆中"活"(低虚拟内存地址)
  4. 传递给函数时,数组会衰减为指针
  5. 数组无法调整大小; 指针可以

总的来说,数组似乎比指针更聪明(但功能更少)(他们知道它们有多大,它们有多少元素,并且它们具有自动内存管理).


问题

  1. 数组如何"知道"它们有多大?这是如何实现的?
  2. 一般来说,如何用C语言实现数组?(编译器是这样做的,还是内核?

Joh*_*ode 7

数组如何“知道”它们有多大?这是如何实施的?

数组知道它们有多大——没有与数组关联的元数据来指示大小(或类型,或其他任何东西)。 在翻译期间,编译器知道数组有多大,并且sizeof在那个时候处理依赖于该知识的任何事情(指针算术、运算等)。一旦生成机器代码,数组就只是一块愚蠢的内存 - 没有办法在运行时通过查看数组对象本身来确定数组有多大(除了可变修改的类型,如可变长度数组,sizeof运算是计算的在翻译期间,而不是运行时)。

一般而言,C语言中数组是如何实现的?(是编译器这样做,还是内核这样做?

数组只不过是相同类型对象的连续序列。对于声明

T arr[N]; // for any type T
Run Code Online (Sandbox Code Playgroud)

你得到

     +---+
arr: |   | arr[0]
     +---+
     |   | arr[1]
     +---+
     |   | arr[2]
     +---+
      ...
     +---+ 
     |   | arr[N-1]
     +---+
Run Code Online (Sandbox Code Playgroud)

没有arr独立于数组元素本身的对象,也没有为大小、起始地址、类型或其他任何内容留出任何元数据。

下标操作arr[i]定义*(arr + i)给定阵列的起始地址,偏移-i元件(而不是字节!从该地址和取消引用的结果)。

您是正确的,数组不是指针 - 但是,除非它是sizeof或 一元运算&符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则数组类型的表达式将被转换(“衰减”)到指针类型的表达式,表达式的值将是数组第一个元素的地址(同样,这都是在翻译期间完成的,而不是在运行时完成)。

因此,当您编写类似 时x = arr[i];,编译器会将表达式转换arr为指针值,因此下标操作有效。

与此相反,当你写ap = &arr;,编译器并没有转换arr为指针类型。结果仍然与第一个元素的地址相同,但类型不同 - 而不是T *,类型是T (*)[N],或“指向 N 元素数组的指针T”。

  • @JeelShah:[C 2011 语言定义,在线草案](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)。对于不懂语言的人来说,这不是一个很好的*学习*资源,但它是我上面写的所有内容的基础。公平地说,语言标准没有明确*禁止*将任何元数据存储为数组对象的一部分,但同时它没有提供任何访问所述元数据的方法。拥有这样的元数据块绝对不符合 C 的精神。 (2认同)

Sou*_*osh 5

数组如何"知道"它们有多大?这是如何实现的?

编译器知道这一点.

一般来说,如何用C语言实现数组?(编译器是这样做的,还是内核?

编译器.

================================================== ========================

您需要关注的一点是,数组是一种类型.它是派生类型.

引用C11,章节§6.2.5/ P20,

数组类型描述了具有特定成员对象类型的连续分配的非空对象集,称为元素类型.只要指定了数组类型,元素类型就应该是完整的.数组类型的特征在于它们的元素类型和数组中的元素数.[...]

所以,本质上,就像编译器知道有关大小的任何其他类型的,它知道数组类型的大小,也.

总大小是根据元素类型的大小乘以该数组中元素的数量计算的.


sep*_*p2k 5

  1. 数组的类型包含其大小(作为编译时常量)及其成员类型.因此,由于编译器知道所有变量的类型,它只能计算sizeof(the_array)sizeof(array_type.element_type) * array_type.element_count.

  2. 在内存分配等方面,它们只是像任何其他变量一样对待:

    如果声明数组类型的自动变量,则会将sizeof(the_array_type)字节数添加到堆栈帧的大小.因此,当输入函数时,堆栈指针增加足以存储数组的内容,并且当函数退出时,它将减少相同的量.

    如果声明具有静态持续时间的变量,sizeof(the_array_type)则将在二进制数据段中保留.

    同样,这与处理任何类型的所有变量的方式相同.重要的是,数组只包含其元素,因此其大小是其内容的大小,而指针仅指向其元素,其大小完全独立于其指向的内容.

    当用作外部的r表达式时sizeof,数组的名称只是编译为其地址(并键入为指针).

    编译器是这样做的,还是内核?

    所有这些都是由编译器完成的.