【问题标题】:Is memory allocated for array variable as well to store the address of the first element in the array? [duplicate]是否也为数组变量分配了内存来存储数组中第一个元素的地址? [复制]
【发布时间】:2018-01-21 07:46:42
【问题描述】:
void func()
{
   int arr[5]={1,2,3,4,5};
   int variable = arr[1];       //variable will be set to 2
}

当这个程序执行时,将在虚拟地址空间的堆栈区域中分配相当于 5 个整数的连续内存,如下所示的 5 个整数数组。

   +---+
   |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
   |   | a[3]
   +---+
   |   | a[4]
   +---+

虚拟地址空间(在代码/数据/堆栈/堆段中)是否有空间分配给数组变量arr本身而不是5个整数的数组? p>

如果没有分配,那么-

考虑代码 sn-p - int variable = arr[1];,它会被编译器转换为*(arr+1),并将出现在地址空间的代码段中,只读。 p>

在每次函数调用func 期间,如何将arr 计算为数组arr 的起始地址?由于每次调用函数时调用堆栈的地址都可能发生变化,因此数组arr 的地址也会发生变化。 那么如果没有单独的空间给数组变量arr这怎么实现呢?

【问题讨论】:

  • 它以已知偏移量分配在堆栈上。
  • 你的图表显示了arr的内存分配——它在内存中连续五个ints
  • 是的,每次进入函数时,数组都可以分配到内存中的不同位置(在堆栈上,但“堆栈”被视为实现细节)。那有什么问题?为什么*(arr+1) 必须是一个常量表达式,在编译时求值?为什么获取指向堆栈位置的指针会出现问题?该标准仅告诉您数组在离开其范围后不再存在,并且您不应该让指针超出范围。但是您的问题似乎错误地假设指向事物的指针必须是编译时常量。
  • @DarshanL int arr[5]; 声明并定义了一个名为arr 的数组对象,其大小为5 * sizeof(int),地址为&arr,就像int variable 声明和定义一个@ 987654341@ 对象名为variable,其大小为1 * sizeof(int),位于地址&variable。第一个重复目标阐明了arr 不是指针,就像variable 不是指针一样。当您意识到 arrvariable 在分配方面的行为完全相同时,您就会发现第二个问题的答案就足够了。
  • 我在这里链接了 2 个问题,因为您确实有 2 个问题。第一个Is an array name a pointer? 回答了这个问题~ 是否为数组本身分配了内存(即你的意思是它是一个指针)=> 没有。然后第二个问题回答:how do compilers assign memory addresses to variables?你的questoin的第二部分。

标签: c pointers memory-address


【解决方案1】:

在每次函数调用func 期间,如何将arr 计算为数组arr 的起始地址?

在 x86 架构上的典型实现中,arr 的位置将取决于 堆栈指针 (esp) 或 帧指针 (@ 987654326@),即:它将通过应用固定偏移量根据那些指针寄存器之一来定义。

每次调用func() 可能不同的是这些指针寄存器的,而不是如何根据这些来定义arr(即: 固定偏移量)。

【讨论】:

  • 好的。所以意思是说代码 sn-p - *(arr+1) 进一步合成为 *((registerPointingToStackFrame_+_offsetToArrayInTheFunc) + 1),对吧?因此,当指令执行时,它会从特定寄存器中获取堆栈帧的起始地址,并添加偏移量以到达数组的起始索引,甚至无需将其(数组的地址)显式存储在某些内存中。
  • @DarshanL 函数调用之间可能会发生变化的是 registerPointingToStackFrame_ 的值。 _offsetToArrayInTheFunc 在编译时生成并且是固定的。我建议您在程序集中查看生成的代码。
  • @DarshanL “甚至没有将它(数组的地址)显式存储在某个内存中”-> 在 x86 架构上它不需要(甚至不需要将它存储在另一个 CPU 寄存器中) ,因为 arch 支持特定的寻址模式:“基数加索引加偏移量”。就像:[BaseReg+Offset+IndexReg]Offset 是常数)。这样就可以将要访问的数组元素的地址直接编码在指令中。
【解决方案2】:

编译器构造中有一个叫做符号表的东西。符号表存储名称arr 和地址(可能是基于进程地址空间的偏移量)。然后,每当编译器看到这个arr[1] 时,它就会生成必要的汇编代码,以便从*(arr+1) 所指的内存地址获得MOVLOAD 指令。 +1 的算术是由编译器确定它指向的类型来完成的。而符号表用于保存变量的内存地址信息。 (例如:- 在函数的情况下,分配给它的堆栈帧决定了我们以arr 的名称指定的连续内存的起始地址 - 这就是为什么它也会随着分配的内存而改变对函数的每次调用可能会有所不同)。

【讨论】:

    猜你喜欢
    • 2021-06-29
    • 2022-07-21
    • 2018-10-26
    • 2017-10-09
    • 2018-09-19
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多