数组特别是不 指针。可以肯定的是, lvalues 似乎都包含(1维)虚拟内存中某个位置的(1维)坐标。但请考虑这个例子。
std::vector< int > data{ 5, 3, 8, 4, -1, 1, 11, 9, 6 };
auto chosen_iter = std::find( data.begin(), data.end(), -1 );
std::swap( *chosen_iter, *( data.end() - 1 ) );
std::partial_sort( data.begin(), chosen_iter, data.end() - 1 );
std::swap( *chosen_iter, *( data.end() - 1 ) );
std::sort( chosen_iter + 1, data.end() );
想到的实际差异是:
#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()
)总的来说,数组似乎比指针更聪明(但功能更少)(他们知道它们有多大,它们有多少元素,并且它们具有自动内存管理功能)。
问题
答案 0 :(得分:5)
数组如何“知道”它们有多大?这是如何实现的?
编译器知道这一点。
一般来说,如何用C语言实现数组? (编译器是这样做的,还是内核?
编译器。
=============================================== ===========================
您需要关注的一点是,数组是类型。它是派生类型。
引用C11
,章节§6.2.5/ P20,
数组类型描述了一个连续分配的非空对象集 特定的成员对象类型,称为元素类型。元素类型应为 指定数组类型时完成。数组类型的特点是它们的 元素类型和数组中元素的数量。 [...]
因此,就像编译器知道任何其他类型的 size 一样,它本身也知道数组类型的大小。
总大小是根据元素类型的大小乘以该数组中元素的数量计算的。
答案 1 :(得分:5)
数组的类型包含其大小(作为编译时常量)及其成员类型。因此,由于编译器知道所有变量的类型,因此可以将sizeof(the_array)
计算为sizeof(array_type.element_type) * array_type.element_count
。
就内存分配等而言,它们只是像任何其他变量一样对待:
如果声明数组类型的自动变量,则会将sizeof(the_array_type)
个字节添加到堆栈帧的大小。因此,当输入函数时,堆栈指针的增加足以存储数组的内容,当函数退出时,它会减少相同的数量。
如果声明一个具有静态持续时间的变量,则sizeof(the_array_type)
将保留在二进制数据段中。
同样,这与处理任何类型的所有变量的方式相同。重要的是简单地说一个数组包含它的元素,所以它的大小是它的内容的大小,而一个指针只指向它的元素,它的大小完全独立于它指向的内容。
当用作sizeof
之外的r表达式时,数组的名称只是编译为其地址(并键入为指针)。
编译器是这样做的,还是内核?
所有这些都是由编译器完成的。
答案 2 :(得分:3)
数组如何“知道”它们有多大?这是如何实现的?
数组不知道它们有多大 - 没有与数组相关联的元数据来指示大小(或类型或其他任何内容)。 在转换期间,编译器知道数组有多大,并且当时处理依赖于该知识的任何事物(指针算术,sizeof
操作等)。一旦生成了机器代码,数组就只是笨拙的内存块 - 没有办法通过查看数组对象本身来判断在运行时数组的大小(除了可变修改的类型,如变量) -length数组,T arr[N]; // for any type T
操作在翻译期间计算,而非运行时间。)
一般来说,如何用C语言实现数组? (编译器是这样做的,还是内核?
数组只不过是相同类型的连续对象序列。声明
+---+
arr: | | arr[0]
+---+
| | arr[1]
+---+
| | arr[2]
+---+
...
+---+
| | arr[N-1]
+---+
你得到了
arr
没有arr[i]
对象独立于数组元素本身,也没有任何元数据可以放在任何地方,包括大小,起始地址,类型或其他任何内容。
下标操作*(arr + i)
定义为i
- 给定数组的起始地址,偏移sizeof
个元素(不是字节!< / em>)从该地址开始并取消引用结果。
数组不是指针是正确的 - 但是,除非它是&
或一元x = arr[i];
运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,数组类型的表达式将被转换(“衰减”)为指针类型的表达式,表达式的值将是数组的第一个元素的地址(同样,这是全部在翻译过程中完成,而不是在运行时完成
因此,当您编写类似arr
的内容时,编译器会将表达式ap = &arr;
转换为指针值,因此下标操作可以正常工作。
相反,当您编写arr
时,编译器不将T *
转换为指针类型。结果仍然与第一个元素的地址相同,但类型是不同的 - 而不是T (*)[N]
,类型是T
,或“指向N的指针元素数组{{1}}“。