栈内存是怎么由谁分配的?

ghost5216 2018-03-07 08:36:17
栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。

查了一些资料,基本都是这么解释。

这么说正确吗?

该如何理解 “由编译器自动分配释放”? 我的理解的“编译器”就是VC2015或GCC等开发工具,将代码编译成可执行程序。但这完全是一个编译时的概念,跟运行时的栈内存没啥关系啊?

请高手指教!!
...全文
1117 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
ljt51882 2020-02-05
  • 打赏
  • 举报
回复
趙老師說的很好,很深入。
LemintC 2019-12-13
  • 打赏
  • 举报
回复
引用 17 楼 LemintC 的回复:
对于变长栈帧,只有在运行的时候才知道栈可能的大小吧。execve在加载的时候,将栈映射为一个匿名文件,初始长度为0。很多资料都说在运行的时候,系统会自动分配(映射)栈对应的物理内存,一直疑惑的就是,系统到底是怎么分配的? 是使用mmap函数分配的么?如果是的话,是在什么地方分配的呢?
这几天看Linux内核源码情景分析,搞清楚了这个问题了。在ELF文件初始加载的时候,分配的堆栈空间的确是0,在vm_area_struct结构中记录的也是0。后续如果有虚拟地址的访问是未映射的,那么在缺页中断的处理中,会判断该虚拟地址是不是属于栈区间的,如果是就会进行新的内存映射,为栈空间分配一块物理页面;如果是普通的虚拟地址未映射的话,就报告segmentation fault。 问题来了:缺页中断处理程序是如何判断一个未映射的虚拟地址是不是栈空间的呢?检查的准则就是判断这个地址的范围是不是在%esp-32范围内,如果是的话,就认为是访问的是栈地址。如果不是的就报告错误。这是因为在X86处理器中有一条pusha指令,可以一次性将32B的数据压入堆栈中,所以检查的准则就是%esp-32。
LemintC 2019-11-20
  • 打赏
  • 举报
回复
对于变长栈帧,只有在运行的时候才知道栈可能的大小吧。execve在加载的时候,将栈映射为一个匿名文件,初始长度为0。很多资料都说在运行的时候,系统会自动分配(映射)栈对应的物理内存,一直疑惑的就是,系统到底是怎么分配的? 是使用mmap函数分配的么?如果是的话,是在什么地方分配的呢?
schlafenhamster 2019-06-20
  • 打赏
  • 举报
回复
编译器 的 “代码生成”阶段 已经 知道 栈 大小
sichuanwww 2019-06-20
  • 打赏
  • 举报
回复
以讹传讹。
比较准确的说法:编译器确定,操作系统分配管理。
smwhotjay 2018-10-15
  • 打赏
  • 举报
回复
PUSH EBP
MOV EBP,ESP
SUB ESP,4//分配局部变量空间

这个sub esp就是在分配栈内存了。。
函数运行完毕就add esp 或者ret x 来堆栈平衡了。
最近看 老码识途 了解了点机器码
赵4老师 2018-10-10
  • 打赏
  • 举报
回复
计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构……
gaheadus 2018-10-09
  • 打赏
  • 举报
回复 1
我觉得大家没有将两个概念区分开来:栈的生成规则,栈的生成。 编译器只是指定了栈的生成规则,编译器并不生成栈;栈的生成这个动作,是在运行的时候由系统完成的。 栈是在运行的时候由系统创建的,“由编译器自动分配释放”这个说法不正确。 请参考如下两份资料: 1.《深入理解计算机系统(第三版)Randal E.Bryant》1.7.3虚拟内存 2.《Linux_UNIX系统编程手册(上) [德]Michael Kerrisk》6.3进程内存布局
赵4老师 2018-03-12
  • 打赏
  • 举报
回复
/STACK (Stack Allocations) Home | Overview | How Do I | Linker Options The Stack Allocations (/STACK:reserve[,commit]) option sets the size of the stack in bytes. To find this option in the development environment, click Settings on the Project menu. Then click the Link tab, and click Output in the Category box. The Reserve text box (or in the reserve argument on the command line) specifies the total stack allocation in virtual memory. The default stack size is 1 MB. The linker rounds up the specified value to the nearest 4 bytes. The optional value specified in the Commit text box (or in the commit argument on the command line) is subject to interpretation by the operating system. In Windows NT, it specifies the amount of physical memory to allocate at a time. Committed virtual memory causes space to be reserved in the paging file. A higher commit value saves time when the application needs more stack space, but increases the memory requirements and possibly the startup time. Specify the reserve and commit values in decimal or C-language notation. Another way to set the size of the stack is with the STACKSIZE statement in a module-definition (.DEF) file. STACKSIZE overrides the Stack Allocations (/STACK) option if both are specified. You can change the stack after the .EXE file is built by using the EDITBIN tool. EDITBIN Options Home | Overviews An option consists of an option specifier, which is either a dash ( – ) or a forward slash ( / ), followed by the name of the option. Option names cannot be abbreviated. Some options take arguments, specified after a colon ( : ). No spaces or tabs are allowed within an option specification. Use one or more spaces or tabs to separate option specifications on the command line. Option names and their keyword or filename arguments are not case sensitive. EDITBIN has the following options: /BIND /HEAP /LARGEADDRESSAWARE /NOLOGO /REBASE /RELEASE /SECTION /STACK /SUBSYSTEM /SWAPRUN /VERSION /WS /STACK Home | Overviews This option sets the size of the stack in bytes and takes arguments in decimal or C-language notation. The /STACK option applies only to an executable file. /STACK:reserve[,commit] The reserve argument specifies the total stack allocation in virtual memory. EDITBIN rounds up the specified value to the nearest 4 bytes. The optional commit argument is subject to interpretation by the operating system. In Windows NT and Windows 95, commit specifies the amount of physical memory to allocate at a time. Committed virtual memory causes space to be reserved in the paging file. A higher commit value saves time when the application needs more stack space but increases the memory requirements and possibly startup time.
worldy 2018-03-09
  • 打赏
  • 举报
回复
引用 楼主 ghost5216 的回复:
栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。 查了一些资料,基本都是这么解释。 这么说正确吗? 该如何理解 “由编译器自动分配释放”? 我的理解的“编译器”就是VC2015或GCC等开发工具,将代码编译成可执行程序。但这完全是一个编译时的概念,跟运行时的栈内存没啥关系啊? 请高手指教!!
是编译时确定,但是,编译前可以指定预留栈大小
赵4老师 2018-03-09
  • 打赏
  • 举报
回复
栈中的变量通常包括函数参数和函数里声明的临时变量。 栈中的基本变量退出其作用域时,没有谁执行一段代码去释放/销毁/析构它所占用的内存,仅仅是没人再去理会的留在当前栈顶上方的若干遗留下来可被后续压栈操作覆盖的无用数据而已。 而栈中的类变量退出其作用域时,会自动执行其析构函数,……
schlafenhamster 2018-03-09
  • 打赏
  • 举报
回复
http://blog.csdn.net/q_l_s/article/details/52594252 搜索“应用程序加载器”
真相重于对错 2018-03-08
  • 打赏
  • 举报
回复
用词不当,不是由编译器分配,而是由编译器确定
xiaohuh421 2018-03-08
  • 打赏
  • 举报
回复
编译器自动分配释放 指的是 要使用的栈空间, 在编译时, 就已经定好分配 多少, 何时分配与销毁. 这部分栈空间的分配和释放的代码就已经生成好了. 特别是分配大小一般是一个常量生成到代码中了. 但这些代码的执行, 肯定还是要运行才执行的.
赵4老师 2018-03-08
  • 打赏
  • 举报
回复
http://edu.csdn.net/course/detail/2344 C语言指针与汇编内存地址-一.代码要素 http://edu.csdn.net/course/detail/2455 C语言指针与汇编内存地址-二.函数
赵4老师 2018-03-08
  • 打赏
  • 举报
回复
编译器生成汇编代码时,在当前函数开头,添加对应的对sp/esp/rsp(对应16/32/64位堆栈指针寄存器)的值减去所需堆栈内存大小,即对该函数分配(其实是预留)了堆栈内存。 另外,alloc函数也会在堆栈分配内存。 自己在VS IDE中查看不同调用约定时,不同参数个数,不同函数局部变量数量,带不带alloc函数……,16位、32位、64位,……等各种C/C++函数对应汇编指令。 理解讨论之前请先学会如何观察! 计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 多用小脑和手,少用大脑、眼睛和嘴,会更快地学会编程! 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步Debug版对应汇编一行! 单步Debug版对应汇编千行不如单步Release版对应汇编一行! 不会单步Release版对应汇编?在你想单步Release版C/C++代码片断的前面临时加一句DebugBreak();重建所有,然后在IDE中运行。(一般人我不告诉他!单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
ckc 2018-03-08
  • 打赏
  • 举报
回复
看个实例吧 cat b.c #include <stdio.h> int b(); int main() { b(); } int b(){ int a=3,b=5,c; c=a+b; return c; } 很简单的c代码,有一个函数b,有几个局部变量,用gcc -S b.c得到汇编代码 cat b.s .file "b.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc leal 4(%esp), %ecx .cfi_def_cfa 1, 0 andl $-16, %esp pushl -4(%ecx) pushl %ebp .cfi_escape 0x10,0x5,0x2,0x75,0 movl %esp, %ebp pushl %ecx .cfi_escape 0xf,0x3,0x75,0x7c,0x6 subl $4, %esp call b addl $4, %esp popl %ecx .cfi_restore 1 .cfi_def_cfa 1, 0 popl %ebp .cfi_restore 5 leal -4(%ecx), %esp .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .globl b .type b, @function b: .LFB1: .cfi_startproc pushl %ebp ;保留ebp的值 .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp ;ebp=当前栈指针 .cfi_def_cfa_register 5 subl $16, %esp ;栈指针调整,空出一些空间来保留局部变量 movl $3, -4(%ebp) ;空出的地方有一块是局部变量a的 movl $5, -8(%ebp) ;这一块是局部变量b的 movl -4(%ebp), %edx movl -8(%ebp), %eax addl %edx, %eax movl %eax, -12(%ebp) ;a+b的结果放在这里,这是局部变量c的空间 movl -12(%ebp), %eax ;返回c leave ;这一步是恢复esp和ebp,这样才可以用ret正确返回 .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE1: .size b, .-b .ident "GCC: (Debian 4.9.2-10) 4.9.2" .section .note.GNU-stack,"",@progbits 关键是看b:那一部分,那是b函数的开始,我已经加上注释了
schlafenhamster 2018-03-08
  • 打赏
  • 举报
回复
由 程序装载器 load 来 指定 cs es ss ds
oyljerry 2018-03-07
  • 打赏
  • 举报
回复
编译器编译,链接生成binary的时候会标示栈等。程序运行的时候。操作系统会根据分配
FishSuperBear 2021-07-01
  • 举报
回复
@oyljerry 要是没有操作系统是裸机呢

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧