[英]How does the [] operator work?
我正在使用C,但是我认为这是一个更底层的问题,与语言无关。
程序如何使用array [0]或array [6]正确地获取正确的数据,而不管它保存的数据类型是什么? 它在内部存储长度还是要寻找某种分隔符?
编译器知道基础数据类型的sizeof
,并将正确的字节偏移量添加到指针。
a[10]
等效于*(a + 10)
,后者等效于*(10 + a)
,后者又等效于10[a]
,没有开玩笑。
编译器在编译时计算出大小,并在目标代码中对大小进行硬编码。
除了直接回答外,我还想提供一些其他信息。
在Dennis Ritchie的C历史主页上有一篇有趣的文章,其中有很多关于数组,数组索引等方面的文章。
这可能不会直接回答您的问题,但可能会使您对C数组有进一步的了解。
都不:-)
对于数组,编译器知道:(a)数组开始的地址,以及(b)数组包含什么类型的元素(int,float,double等),以及每个元素的长度。
利用这两条信息,找到array[6]
是一个简单的算法问题:从基地址开始,再加上元素大小的6倍。
编译器将替换在编译时固定的数据类型的长度。
int getInt(void * memory, offset)
{
return *((int *)(sizeof(int)*offset + memory))
}
void * chunkOfMemory = malloc(0x1000);
int * intarray = (int *) chunkOfMemory;
printf("%d is equal to %d", getInt(chunkOfMemory, 9), intarray[9]);
编译器在编译时知道数组每个元素的大小。 例如:
int64_t array[5];
...
int64_t a = array[3];
这将被转换为伪汇编代码:
addr <- array
addr <- addr + 3 * sizeof(int64_t)
// ^^^^^^^^^^^^^^^ which the compiler knows is 8
// ^^^^^^^^^^^^^^^^^^^ which the compiler can replace with 24.
a <- *addr
数组的长度无关紧要。
这是编译器的魔力 !
编译器知道数组元素的大小,并使用它来计算正确的地址。
是的,您是对的,甚至更低级的问题,甚至汇编器都有[]
运算符。 这个答案说的很好,但我的解释是:
arr[x]
与*((void *)(&arr) + x * sizeof(arr[0]))
看起来有点复杂,但是生成的代码很简单。 这是因为编译器知道sizeof(arr[0])
并且在编译代码中进行了硬编码,而且(void *)(&arr)
只是一种语言标准,可以保护程序员免受愚蠢的错误,并且在编译代码中没有类型转换。
正如我提到的低级语言一样,还有一件事需要提高。 使用它们,您可以重载运算符,并使它做您想做的任何事情。
不,不是。 它只是在地址array + X*sizeof(TypeOfArrayEl)
处获取/设置元素,因此您可以轻松地越界,并且那时没有人会给您错误。 这就是为什么array[6]
与6[array]
相同的原因
假设数组的类型为int:
int array[12];
[]
运算符将括号内的任何值(乘以数组类型的字节大小)乘以括号外的值。 数组由实现存储为指向其第一项的指针。 因此,上面的数组声明分配了12 * sizeof(int)个字节,并使array
指向第一个。 这会导致像3[array]
这样的古怪的东西给您3[array]
的第三个元素。
无论如何,问题的答案是编译器在编译时查看数组的类型,然后将[]中的值乘以数组持有的类型的大小。
据我所知,如果索引超出范围,C不会给您一个编译时错误。 即使您超出范围,指针也只会为您提供下一个相邻的存储位置。 C唯一需要照顾的是增加多少字节的指针。 如果它是一个整数数组,则指针将针对索引中的每个增量前进2个字节,对于char类型的指针将递增1个字节。
您始终可以访问超出范围但是垃圾数据的位置,而作为程序员,您必须确保访问的数据正确。
我猜这就是自由的代价:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.