返回信息流在vc中写
int s[500][500][500];
void main()
{
}
基本没什么反应...就当没分配内存似的
但是
////////////////
void main()
{
int s[500][500][500];
}
结果直接就死机了...
全局变量就不用分内存吗...实在是晕了..555..
这是一条镜像帖。来源:北邮人论坛 / soft-design / #4915同步于 2006/3/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖
...再问个堆与栈的问题..
lsm041022
2006/3/23镜像同步11 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
#include "a.h"
int s[500][500][500];
void main()
{
int i=s[500][500][500];
}
反编译后在watch里对s监视会发现s已分配了内存的
全局变量不是在栈中分配的 应该是在另一个”不受限制“的全局空间分配的
期待强人详细说明一下
。。。当然要分配了,而且是在进入main函数之前就分配好了
第2个程序是在main函数的栈上分配
debug版的反汇编
{
push ebp
mov ebp, esp
mov eax, 500000200; 1dcd65c8H
call __chkstk
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-500000200] ;分配空间,debug版,不清楚大小怎么确定的
mov ecx, 125000050; 07735972H
mov eax, -858993460; ccccccccH
rep stosd
; 5 : int s[500][500][500];
; 6 : }
mov eax, 500000200; 1dcd65c8H
call __chkstk
这两句分配的吗 ?
记得栈中分配直接修改esp就可以了吧..
chkstk显然就是check stack嘛
可能是因为你定义的s太大 所以编译器加了这么一条 好像和选项/Ge有关
上面是。net生成的
lea edi, DWORD PTR [ebp-500000200]
mov ecx, 125000050; 07735972H
mov eax, -858993460; ccccccccH
rep stosd 这几句是分配并初始化 debug版 release版怎样不清楚
这个问题问得好,涉及到PE,栈,线程TEB等多个环节,
一会我整理一下再解释。。。。。。
【 在 lsm041022 (无忌(思雨...)) 的大作中提到: 】
: 在vc中写
: int s[500][500][500];
: void main()
: ...................
【 在 flyingkisser 的大作中提到: 】
: 这个问题问得好,涉及到PE,栈,线程TEB等多个环节,
: 一会我整理一下再解释。。。。。。
.......................
寒......
我的解释只对于win32平台成立。
我的C编译器是vc6.0的cl.exe
首先有两点是肯定的
1.初始化的全局变量的空间是编译进PE文件的,具体是在PE文件的某个节区。
具体在哪个节区,这个节区的名字,由编译器控制
未初始化的全局变量不编译进PE文件节区
2.在函数内部像 int a;这样声明的变量,其内容是在栈中分配的
--------程序1------------------
int s[500][500][500];
void main()
{
}
-----------------------------
说明:全局变量没有编译过PE节区,栈里也没有分配内存的操作
---------程序2------------------
void main()
{
int s[500][500][500];
}
-------------------------------
在栈中定义了int s[500][500][500];
500*500*500*4字节,即0x1DCD6500字节,476M
要在栈中分配476M的内容空间。
像楼上分析的一样,
00401003 |. B8 0065CD1D mov eax, 1DCD6500
00401008 |. E8 13000000 call test.00401020
这段代码的作用是先检测栈中能否分配出1DCD6500字节的内存,
如果可以的话,就分配,
如果不行的话,会引起读无效页的异常,转而执行SEH中的异常处理程序,
即windows报错,退出
00401020 /$ 51 push ecx
00401021 |. 3D 00100000 cmp eax, 1000
;---ecx指向栈顶,
00401026 |. 8D4C24 08 lea ecx, dword ptr ss:[esp+8]
0040102A |. 72 14 jb short test.00401040
;--栈由高地址向低地址生长,所以要用sub,一次检测4K,正好是1页
0040102C |> 81E9 00100000 /sub ecx, 1000
00401032 |. 2D 00100000 |sub eax, 1000
00401037 |. 8501 |test dword ptr ds:[ecx], eax
00401039 |. 3D 00100000 |cmp eax, 1000
0040103E |.^ 73 EC \jnb short test.0040102C
;--检测通过的话,通过mov esp,ecx在栈中分配内存
00401040 |> 2BC8 sub ecx, eax
00401042 |. 8BC4 mov eax, esp
00401044 |. 8501 test dword ptr ds:[ecx], eax
00401046 |. 8BE1 mov esp, ecx
;--其他操作,退出
00401048 |. 8B08 mov ecx, dword ptr ds:[eax]
0040104A |. 8B40 04 mov eax, dword ptr ds:[eax+4]
0040104D |. 50 push eax
0040104E \. C3 retn
用OD加载这个程序,刚加载时,
它的栈空间的栈起始地址位于TEB的偏移04h的址方,栈顶地址位于TEB偏移08h的地方
(windows的环境切换是以线程为单位的,一个进程至少有一个线程,一个线程对应一个TEB,保存着有关这个
线程的重要信息,其地址在用户地址空间,ring3可访问,即位于fs段选择子在GDT中索引到的段描述符所描述
的段中,这个段很特殊,和平坦模式的段不同,有自己的段起始地址,段界限,其大小不是4G,段基地址在
一个版本的windows下一般是相同的)
我写了一个程序,指定进程PID,分析这个进程的线程信息,分析结果如下:
C:\masm32\source\kmd\thread>tlist 148
Thread 928 Own Process 148 Code begin at 0x77E687B3
Ring0 Stack Begin: 0xF23A9000
End : 0xF23A6000
Ring3 Stack Begin: 0x00130000
End : 0x0012E000
可以看到栈起始于130000,结束于12E000。
为了验证从12e000到130000这段虚拟地址空间有物理内存与之映射,再用我写的另一个程序分析一下:
0x0012E000---0x0012EFFF : 0x071FE000---0x071FEFFF
0x0012F000---0x0012FFFF : 0x09F36000---0x09F36FFF
左边是虚拟地址,右边是它们对应的物理地址
正好是2页,符合上面的分析。
下面再分析windows是如何验证栈空间的,关键代码是以下几句:
0040102C |> 81E9 00100000 /sub ecx, 1000
00401032 |. 2D 00100000 |sub eax, 1000
00401037 |. 8501 |test dword ptr ds:[ecx], eax
00401039 |. 3D 00100000 |cmp eax, 1000
0040103E |.^ 73 EC \jnb short test.0040102C
这个循环开始的时候,ecx放的是栈顶地址,每循环,ecx都要sub掉1页(即4K,0x1000字节)
接着再test [ecx],eax 这个test就是起验证作用的,从上面的分析可以知道,栈的结束于
12e000,当ecx低于这个值时,访问test [ecx],eax 必然要引起页读取异常,因为这个地址
没有物理地址与之映射啊,然后windows捕获这个异常,在IDT中根据中断号找到这个异常对应
的异常处理程序,异常处理程序为访问的虚拟地址映射新的物理内存
就这样一直验证,异常,映射新的物理内存,直到物理内存耗光,页交换文件也用光蛋,windows没招
了,弹出一个对话框,告诉你让这个程序滚蛋吧,这活我不干了。
---------程序3-------------------
#include <stdio.h>
int s[500][500][500]={8};
int main(int argc, char *argv[])
{
return 0;
}
--------------------------------
全局变量初始化了,要写入到PE中,所以把编译器累得快死,这个程序我的机器一编译
cl.exe的上到500多M的内存,没时间和它耗了,只能强行关掉编译器进程
---------程序4-------------------
#include <stdio.h>
int s[500][500]={8};
int main(int argc, char *argv[])
{
return 0;
}
--------------------------------
这下小了点,500*500*4=0xf4240,将近1M吧
编译完,观察PE结构,VirtualSize为0xF49DC,SizeOfRawData的值为F5000
(VirtualSize是指节区的原始大小,即不经过任何处理的大小,有多少变量,占
多少字节,这个值就是多大,所以这个字段就是节区的实际大小
SizeOfRawData的意思是VirtualSize的值按FileAlignment对齐后的大小,
就是指它在硬盘里占用的文件空间大小
FileAlignment默认情况下是500字节)
我的结论的前2条就是一开始肯定的那2条,
第3条就是不要在全局变量或栈中分配那么多内存空间,自己给自己找罪受.....
【 在 flyingkisser (齐天大猫) 的大作中提到: 】
: 这个问题问得好,涉及到PE,栈,线程TEB等多个环节,
: 一会我整理一下再解释。。。。。。