为什么C变量存储在特定的内存位置?

昨天我采访了面试官,询问我存储变量的存储类。

我的回答战争:

Local Variables are stored in Stack.       
Register variables are stored in Register
Global & static variables are stored in data segment.  
The memory created dynamically are stored in Heap.

他问我的下一个问题是:为什么它们被存储在这些特定的内存区域中?为什么 Local variable not 存储在< code> register (虽然我需要一个 auto 变量在我的程序中非常频繁地使用)?或者为什么全局或静态变量 not 存储在 stack 中?

然后我无能为力。请帮帮我。

6
额外 编辑
意见: 1
register 变量可能不存储在寄存器中。
额外 作者 user703016,
局部变量并不总是存储在堆栈中(通常被省略或移动到寄存器中)。寄存器变量并不总是存储在寄存器中(它们通常被移动到堆栈中)。全局(包括静态)变量并不总是存储在数据段中(它们通常放置在文本段或专用存储器映射中)。寄存器变量和非寄存器局部变量之间唯一的区别是编译器拒绝取得寄存器变量地址的程序。
额外 作者 Dietrich Epp,

5 答案

因为存储区决定了变量的 范围生命周期

您根据您的要求选择存储规格,即:
Lifetime 您期望特定变量的持续时间需要保持活跃和有效。
范围: 您希望变量可以访问的范围(区域)。

简而言之,每个存储区都提供了不同的功能,因此您需要各种功能,因此需要不同的存储区

16
额外
:如果我们声明一个变量 fun(){static {int a; a ++;} 然后我要去其他函数(假设 fun2 ),然后我回到 fun()如何保留其以前的值。因为 static 的生​​命周期在 fun()
额外 作者 Rasmi Ranjan Nayak,
我不会淡化这个答案。在微处理器中没有范围和寿命这样的事情。在这个区域中只有一些连续的存储区域是LIFO(堆栈)。
额外 作者 Luka Rahne,
问题是关于这些变量存储在哪里,并且您正在讨论生命周期和范围。这样说就好,因为标准是这样说的。我没有阅读标准,但看起来它不会谈论堆栈和堆,所以你不能使用范围和生命周期来解释它们。
额外 作者 Luka Rahne,
@Als, register 实际上并没有强制要求任何东西。这只是一个提示。
额外 作者 bdonlan,
@RasmiRanjanNayak:是的,这就是它的全部意义。您希望函数中的变量 a 在程序的整个生命周期中保持活动状态,因此您需要选择 static 存储类,编译器存储这个变量的地方并不重要,标准没有真正说出它的任何内容,它是一个实现细节,但是标准要求 static 变量在整个并保持其状态。
额外 作者 Alok Save,
@ralu:范围和生命周期与c/c ++标准通过关键字 >, register 等。标准规定,当你在变量声明中使用这些关键字时,实现应该确保该变量的某些范围和生命周期属性。标准不会说 how其中 保存这些变量或应该如何实现功能。这是一个实现细节。它只是强制用户应该获得与特定存储类相关的属性。
额外 作者 Alok Save,
@bdonlan:斑点。在该列表中的 register 是一个typo.dedeed register 不指定任何东西
额外 作者 Alok Save,
@ralu,像C这样的编程语言为您提供了一个平台的抽象,并且编译后的程序保证有一定的可观察行为来实现这种抽象。如果你不希望你必须用汇编程序进行编程,该程序完全实现了你编程的特定处理器的功能。
额外 作者 Jens Gustedt,

C语言没有定义实际存储变量的地方。但是,它确实定义了三种存储类别:静态,自动和动态。

静态变量是在程序初始化期间(在 main()之前)创建的,并且一直存在,直到程序终止。文件范围('全局')和静态变量属于该类别。虽然这些通常存储在数据段中,但C标准并不要求这样,在某些情况下(例如C解释器),它们可能存储在其他位置,例如堆。

自动变量是在函数体中声明的局部变量。它们是在程序流程到达声明时或之前创建的,并在超出范围时销毁;为递归函数调用创建这些变量的新实例。堆栈是实现这些变量的一种方便的方法,但它又不是必需的。如果你选择了,你也可以在堆中实现自动化,并且它们通常也放在寄存器中。在很多情况下,自动变量会在其生命周期内在栈和堆之间移动。

请注意,自动变量的 register 注释是一个提示 - 编译器没有义务对它做任何事情,事实上许多现代编译器完全忽略它。

最后,动态对象(在C中没有动态变量)是指使用 malloccalloc 或其他类似的分配函数显式创建的值。它们在明确创建时才会生成,并且在明确释放时会被销毁。堆是一个方便的地方放置这些 - 或者说,根据这种分配风格的能力定义堆。但是,再次,编译器实现可以自由地执行任何想要的操作。如果编译器可以执行静态分析来确定动态对象的生命周期,那么它可能会将其移至数据段或堆栈(但是,很少有C编译器会执行这种“转义分析”)。

这里关键的一点是,C语言标准只定义了给定值存在的时间。并且在此生命的最小范围 - 它可能会比需要的时间更长。具体如何将它放在内存中是一个主题,其中语言和库实现具有很大的自由度。

12
额外
@SwanandPurankar,K&R已过时。尽管如此,这是一个很好的概念指导,但是如果你想在技术上正确地处理这样的事情,最好参考实际的规范。 C语言规范不会谈论'堆'。是否将某些东西放在堆上是实现的选择。但是,大多数实现不会将静态数据放在堆上(但有少数例外,例如C解释器)。
额外 作者 bdonlan,
嘿@bdonlan,我在采访中回答了同样的问题,即Statics存储在Heap上。访员就像“你错了......你有没有回答过K&R和等等......”那么我的回答错了吗? K&R根据什么?
额外 作者 Swanand,

它实际上只是一个方便的实现细节。

如果他愿意,编译器可以根据需要在堆上生成局部变量。

在堆栈中创建它们更容易,因为当离开某个函数时,可以根据堆栈的增长方向使用简单的加/减操作来调整帧指针,从而自动释放用于下一个函数的已用空间。然而,在堆上创建当地人将意味着更多的管家工作。

另一点是不能在堆栈上创建局部变量,如果编译器认为这是更合适的并且有足够的寄存器可以存储和使用它们,那么它们可以存储并用于寄存器中。

1
额外

在大多数情况下,局部变量都存储在寄存器中,因为在进行函数调用时,寄存器被从堆栈中推入并进行调用它看起来像是在堆栈中。

实际上没有寄存器变量这样的情况,因为它只是C中一个很少使用的关键字,它告诉编译器尝试将它放入寄存器中。我认为大多数编译器都忽略了这个关键字。

那为什么问你更多,因为他不确定你是否深刻理解主题。事实是寄存器变量实际上是在堆栈上。

0
额外
大多数变量默认情况下进入注册。寄存器只能用于告诉编译器这样的变量优先进入寄存器。
额外 作者 Luka Rahne,
“当你进行函数调用时,寄存器被推...” - 有些函数调用可能包含函数参数,其他函数可能只有被调用的函数推动,如果它觉得需要更多的寄存器,并且在某些情况下,CPU支持它们自己的机制保存和恢复寄存器无堆栈(例如UltraSparcs)。此外,拥有本地数组和文本数据并不罕见 - 它们不太适合寄存器,所以“大多数情况下,本地......在寄存器中”没有澄清就无法提供见解。然后“寄存器变量实际上处于堆栈中” - 在说明非代码寄存器变量在寄存器中之后!
额外 作者 Tony Delroy,
register 不是无用的,它表示 auto 加上取消变量地址的封锁。因此,这是针对指针参数的 restrict 的优化提示,例如,
额外 作者 Jens Gustedt,
你在混合不同的概念。处理器的寄存器和C语言的 register 关键字。我只是在说关键字指定的是什么,它与变量是否在寄存器(处理器)中实现或没有关系。如果你想要它只是一个用词不当的地方,用 addressless 替换 register (关键字),并且你更接近它在C中的含义。
额外 作者 Jens Gustedt,

在嵌入式系统中,我们有不同类型的存储器(只读非易失性(ROM),读写非易失性(EEPROM,PROM,SRAM,NVRAM,闪存),易失性(RAM))以及我们有不同的要求并且在电力循环之后也坚持,在电力循环之后可以改变并且持续,可以随时改变)我们所拥有的数据。我们有不同的部分,因为我们必须乐观地将我们的数据要求映射到不同类型的可用存储器。

0
额外