查了一些资料,基本都是这么解释。
这么说正确吗?
该如何理解 “由编译器自动分配释放”? 我的理解的“编译器”就是VC2015或GCC等开发工具,将代码编译成可执行程序。但这完全是一个编译时的概念,跟运行时的栈内存没啥关系啊?
请高手指教!!
10 个解决方案
#1
编译器编译,链接生成binary的时候会标示栈等。程序运行的时候。操作系统会根据分配
#2
看个实例吧
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函数的开始,我已经加上注释了
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函数的开始,我已经加上注释了
#3
编译器生成汇编代码时,在当前函数开头,添加对应的对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执行停在该断点处的时候。
另外,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执行停在该断点处的时候。
#4
http://edu.csdn.net/course/detail/2344 C语言指针与汇编内存地址-一.代码要素
http://edu.csdn.net/course/detail/2455 C语言指针与汇编内存地址-二.函数
http://edu.csdn.net/course/detail/2455 C语言指针与汇编内存地址-二.函数
#5
编译器自动分配释放
指的是 要使用的栈空间, 在编译时, 就已经定好分配 多少, 何时分配与销毁. 这部分栈空间的分配和释放的代码就已经生成好了. 特别是分配大小一般是一个常量生成到代码中了.
但这些代码的执行, 肯定还是要运行才执行的.
指的是 要使用的栈空间, 在编译时, 就已经定好分配 多少, 何时分配与销毁. 这部分栈空间的分配和释放的代码就已经生成好了. 特别是分配大小一般是一个常量生成到代码中了.
但这些代码的执行, 肯定还是要运行才执行的.
#6
用词不当,不是由编译器分配,而是由编译器确定
#7
由 程序装载器 load 来 指定 cs es ss ds
#8
栈中的变量通常包括函数参数和函数里声明的临时变量。
栈中的基本变量退出其作用域时,没有谁执行一段代码去释放/销毁/析构它所占用的内存,仅仅是没人再去理会的留在当前栈顶上方的若干遗留下来可被后续压栈操作覆盖的无用数据而已。
而栈中的类变量退出其作用域时,会自动执行其析构函数,……
栈中的基本变量退出其作用域时,没有谁执行一段代码去释放/销毁/析构它所占用的内存,仅仅是没人再去理会的留在当前栈顶上方的若干遗留下来可被后续压栈操作覆盖的无用数据而已。
而栈中的类变量退出其作用域时,会自动执行其析构函数,……
#9
是编译时确定,但是,编译前可以指定预留栈大小
#1
编译器编译,链接生成binary的时候会标示栈等。程序运行的时候。操作系统会根据分配
#2
看个实例吧
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函数的开始,我已经加上注释了
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函数的开始,我已经加上注释了
#3
编译器生成汇编代码时,在当前函数开头,添加对应的对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执行停在该断点处的时候。
另外,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执行停在该断点处的时候。
#4
http://edu.csdn.net/course/detail/2344 C语言指针与汇编内存地址-一.代码要素
http://edu.csdn.net/course/detail/2455 C语言指针与汇编内存地址-二.函数
http://edu.csdn.net/course/detail/2455 C语言指针与汇编内存地址-二.函数
#5
编译器自动分配释放
指的是 要使用的栈空间, 在编译时, 就已经定好分配 多少, 何时分配与销毁. 这部分栈空间的分配和释放的代码就已经生成好了. 特别是分配大小一般是一个常量生成到代码中了.
但这些代码的执行, 肯定还是要运行才执行的.
指的是 要使用的栈空间, 在编译时, 就已经定好分配 多少, 何时分配与销毁. 这部分栈空间的分配和释放的代码就已经生成好了. 特别是分配大小一般是一个常量生成到代码中了.
但这些代码的执行, 肯定还是要运行才执行的.
#6
用词不当,不是由编译器分配,而是由编译器确定
#7
由 程序装载器 load 来 指定 cs es ss ds
#8
栈中的变量通常包括函数参数和函数里声明的临时变量。
栈中的基本变量退出其作用域时,没有谁执行一段代码去释放/销毁/析构它所占用的内存,仅仅是没人再去理会的留在当前栈顶上方的若干遗留下来可被后续压栈操作覆盖的无用数据而已。
而栈中的类变量退出其作用域时,会自动执行其析构函数,……
栈中的基本变量退出其作用域时,没有谁执行一段代码去释放/销毁/析构它所占用的内存,仅仅是没人再去理会的留在当前栈顶上方的若干遗留下来可被后续压栈操作覆盖的无用数据而已。
而栈中的类变量退出其作用域时,会自动执行其析构函数,……
#9
是编译时确定,但是,编译前可以指定预留栈大小