Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1477926
  • 博文数量: 148
  • 博客积分: 2234
  • 博客等级: 大尉
  • 技术积分: 3225
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-17 21:34
个人简介

未来很长。

文章存档

2017年(7)

2016年(4)

2015年(1)

2014年(6)

2013年(31)

2012年(99)

分类: C/C++

2012-08-04 20:55:53

关于C++中局部变量在栈中分配被无数人提到,本人也查看了别人的很多资料。我对这个问题的研究开始于高质量程序设计指南中关于const常量是否占内存空间的说法,原文为:在C语言中,用const定义的常量其实是值不能修改的变量,因此会给它分配存储空间;但是在C++中,const定义的常量要具体情况具体对待:对于基本数据类型的常量,编译器会把它放到符号表中而不分配存储空间,而ADT/UDT的const对象则需要分配存储空间(大对象)。还有一些情况下也需要分配存储空间,例如强制声明为extern的符号常量或取符号常量的地址等操作。读到这段话,我想要不我在VS2008下写个程序试验一下,于是这一试验,就引发了一系列问题。我在VS2008中编写的程序如下所示:

int main(int argc, char* argv[])
{
 char ch=0;
 int e=5;
 int f=6;
 const int a=7;
 int b=8;
 int c=9;
 printf("%d\n",&ch);
 printf("%0x\n",&e);
 printf("%0x\n",&f);
 printf("%0x\n",&b);
 printf("%0x\n",&c);
  cin>>ch;
 cin>>ch;
 return 0;

}

该程序实际上非常简单,就是定义了几个整数和一个常量,然后输出几个整数的地址,我得到的结果为:

C++中局部变量在栈中分配(及对应的汇编代码) - 蓝天白云 - 乖乖女

 从图中可以看出e、f整型变量之间相距12个字节,也就是在VS2008中给每个整型变量会分配12个字节,其实数据占4个字节,其余8个字节填充为CCH,为什么这么做暂时还不清楚。但是f和b之间却相差了24个字节,所以我推断实际上编译器给常量a也分配了地址。实际上确实编译器给常量也分配了地址。

我是怎么知道的呢?当我看到如图运行结果时,我就想让我看看内存中的栈区吧,我就知道到底是怎么回事了,可是由于比较菜鸟,所以不知道怎么在VS2008下看内存,于是百度啊,找到了方法。具体为:

首先在程序中设置一个断点,为了方便调试,我把断点加到了最后。然后按F5或点启动调试按钮,这时程序就进入到了调试状态。第三步:调试—>窗口—>内存—>内存1就可以看到内存中的数据了。其实你会发现调试窗口中还有好多东西,比如反汇编啊,寄存器啊之类的。这些都可以在调试中用到。比如点击反汇编则会进入程序对应的汇编代码,那个汇编代码还是很有意思的,待会再细细分析一下。在反汇编代码中也可以设置断点进行单步调试来一条一条执行汇编指令。

注意:在VS2008没有进入调试状态时,调试—>窗口中是不会有那些子菜单出现的,所以得先设一个断点

现在我已经打开了内存窗口,可是密密麻麻从哪里看起呢,于是就想到了汇编代码,先看看这段程序汇编代码吧:

 汇编代码第一行:push ebp //将基址指针寄存器入栈,基址指针寄存器用来确定堆栈段中某一存储单元的地址,这是执行每个函数之前都要进行的保护现场的工作

第二行: mov ebp,esp //将堆栈指针移入基址指针

第三行:sub esp,108h//堆栈指针减去108h,实际上就是给当前程序分配108h个内存空间

前三行汇编代码实际上是分配内存的前期工作

接下来的三行是保护基址寄存器EBX、源变址寄存器SI和目的变址寄存器DI。在执行push操作时堆栈指针ESP进行是减法操作,每有一个寄存器入栈,ESP都会减去4。

在接下来的四行实现变量内存空间的初始化,初始化为CCH

lea   edi,[ebp-108h]//将要初始化的内存空间的首地址送到目的变址寄存器EDI中。LEA是有效地址送寄存器指令

mov  ecx ,42H//将42H送计数器CX中,为下一条REP指令做好准备

mov eax ,0CCCCCCCCH //将要初始化的数值送到EAX寄存器中

rep stos dword ptr es:[edi]//实现初始化操作  REP指令用于与其他指令配合起来实现重复传送功能,传送次数由ECX决定,每传送一次数据,ECX都将减1,等ECX减为0时传送结束。在这里给ECX赋值为42H,说明会传送42H次,而每次都会传送四个字节,所以会初始化42H乘以4个内存单元,也就是108H个内存单元。stos是存入串指令,该指令会把AX中的数据存入DI寄存器所指向的内存单元。stos指令经常用于初始化一个缓冲区。stos指令的指令格式为:stos 目标地址。在这条指令中目标地址为:es:[edi]。在这里dword ptr为属性操作符,用于表示进行的是双字传送,而不是字或者字节。

接下来就是具体变量的初始化: mov  byte ptr[ch],0  //将ch所对应的内存单元初始化为0,在这里ch代表变量ch的内存地址,方括号用来表示是直接寻址方式。可以看出ch初始化时只初始化了一个字节,但是实际上ch占据了四个字节。同样整型变量初始化时只初始化4个字节,但是分配了12个字节的空间。从汇编代码可以看出实际上常量a和其余变量一样都在栈中分配了存储空间。

我们通过图来分析一下这个程序占用的栈空间:

 

通过分析内存可以看出该程序共占用了108H字节单元的内存地址,字符ch的内存地址为0012FF63H,之前有四个字节没有用,我也不知道编译器为什么要这样安排,字符e的内存地址为0012FF54H。常量a的地址为0012FF3CH。为了const占不占用空间,我好像纠结了太久,花了好久时间,实际上我也知道只要会用就行不用太较真,可是哎,就是个爱较真的人。我是个菜鸟,如果有分析不对的,如果有不同见解的欢迎给我留言啊!

 分析到这不知该如何往下写了,先就这样吧,总之是学会了一点点汇编和变量分配以及调试。

阅读(8995) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

xumin3307742332014-01-15 13:47:51

和我的观点一样。我的是gcc 4.8.
http://blog.csdn.net/xumin330774233/article/details/18261115