软件下载 | 资讯教程 | 最近更新 | 下载排行 | 一键转帖 | 发布投稿
您的位置:最火下载站 > 电脑教程 > 编程开发 > C/C++开发 > 理解C++变量存储模型

理解C++变量存储模型

理解程序内存一文中我们介绍了普通程序运行时在内存中的布局,下面我们专门针对C++源代码以WinDbg为工具分析下C++程序的变量存储模型, 要理解下面的知识,请先看懂理解程序内存一文。

下面我们尝试分析C++变量的存储模型, 我们的测试程序非常简单:

Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
#include <iostream>

using namespace std;



const char* global_const_string = "hello world";

int global_int = 20;

static int global_static_int = 30;

int main()

{

static int local_static_int = 100;

int local_int = 200;

int* pValue = new int(300);

cout << global_const_string << global_int

<< global_static_int << local_static_int

<< local_int << *pValue;

delete pValue;

system("pause");

return 0;

}

可以看到我们上面对程序虽然简单,但是基本包括了所有的变量类型,包括静态的,常量的,全局的,本地的,还有new出来的,下面我们依次分析每个变量所属的存储区域。

我们直接用WinDbg以源码的方式调试我们的测试程序consoleTest.exe.
首先我们分析下consoleTest.exe模块的起始地址及内部数据节的分布情况, 通过!address命令

Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
* 400000 401000 1000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"

|- 401000 41d000 1c000 MEM_IMAGE MEM_COMMIT PAGE_EXECUTE_READ Image "ConsoleTest.exe"

|- 41d000 422000 5000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"

|- 422000 426000 4000 MEM_IMAGE MEM_COMMIT PAGE_WRITECOPY Image "ConsoleTest.exe"

|- 426000 427000 1000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"

可以看到consoleTest.exe模块在内存中的起始地址是0x400000, 接下来可以通过!dh 0x400000分析它内部的数据节分布, 并且最终我们可以得出如下结论:
地址 400000 - 401000 : PE文件头,属性是只读
地址 401000 - 41d000 : .text, 属性是只读可执行,表示代码节
地址 41d000 -  422000 : .rdata, 属性是只读, 表示只读数据
地址 422000 -  426000 : .data, 属性是写入时拷贝,表示可读写数据
地址 426000 - 427000 : .rsrc, 属性是只读,表示资源节

通过!address -f:stack命令我们可以看到:

Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
0:000> !address -f:stack

BaseAddr EndAddr+1 RgnSize Type State Protect Usage

-------------------------------------------------------------------------------------------

40000 13d000 fd000 MEM_PRIVATE MEM_RESERVE Stack [8b0.1d0; ~0]

13d000 13e000 1000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE|PAGE_GUARD Stack [8b0.1d0; ~0]

13e000 140000 2000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE Stack [8b0.1d0; ~0]

可以看到我们主线程的堆栈起始地址是: 13e000 - 140000

接下来我们首先分析所有全局变量的存储区域, 通过x consoletest!global*命令,让调试器列出所有在consoletest模块中global开头的调试符号:

Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
0:000> x consoletest!global*
00422000 ConsoleTest!global_const_string = 0x0041d1dc "hello world"
00422004 ConsoleTest!global_int = 0n20
00422008 ConsoleTest!global_static_int = 0n30
004238a0 ConsoleTest!global_locale = 0x00000000

通过分析我们可以看到我们的3个全局变量global_const_string, global_int, global_static_int全都分布在422000 - 426000之间的.data可读写数据节中。
而global_const_string所指向的内容

0x0041d1dc "hello world"

则分布在41d000 -  422000 之间的.rdata只读数据节中,这个结论也符合我们平时关于全局变量存储区域的理解。

下面我们再尝试分析局部变量的存储区域,再main函数内部cout的地方设置断点,然后让程序运行到此, 然后输入dv /t /i /v命令查看所有局部变量, 可以看到
Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
0:000> dv /t /i /v
prv local 0042200c int local_static_int = 0n100
prv local 0013ff70 int local_int = 0n200
prv local 0013ff74 int * pValue = 0x02248ff8
我们可以看到local_static_int也分布在422000 - 426000之间的.data可读写数据节中, 而local_int和pValue则都存储在13e000 - 140000之间的堆栈区域上。

而指针pValue所指向的地址0x02248ff8我们可以通过!address 0x02248ff8命令来分析, 结果是:
Copy to ClipboardLiehuo.Net Codes引用的内容:[www.veryhuo.com]
0:000> !address 0x02248ff8

Usage: Heap
Allocation Base: 021d0000
Base Address: 02248000
End Address: 02249000
Region Size: 00001000
Type: 00020000 MEM_PRIVATE
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
More info: !heap -p 0x21d1000
More info: !heap -p -a 0x2248ff8
可以看到地址0x02248ff8是在堆(heap)上面。

通过上面的分析,我们验证了平时C++书上关于各种类型变量存储区域的假设,简单来说就是全局变量和静态变量会被编译到可执行文件的数据节(分只读和可读写)中, 非静态的局部变量则分配在堆栈(stack)上,而new(malloc)出来的内存则分配在堆(heap)上。
    相关阅读
    栏目导航
    推荐软件