在帖子如何判断一个C++对象是否在堆栈上 中, 又有人提出如何判断一个C++对象是否在堆上。

其实我们可以参照那个帖子的方法类似实现,我们知道堆就是Heap,在windows上我们可以通过GetProcessHeaps来得到所有的堆句柄,而我们这里只要知道Windows上的Heap Handle,其实就是堆的起始地址,就可以写如下代码了。

#include <iostream>
#include <windows.h>

using namespace std;

BOOL IsObjectOnHeap(LPVOID pObject)
{
    BOOL bRet(FALSE);
    DWORD dwHeaps = GetProcessHeaps(0, NULL);
    LPHANDLE pHeaps = new HANDLE[dwHeaps];
    if(pHeaps != NULL)
    {
        MEMORY_BASIC_INFORMATION mi = {0};
        GetProcessHeaps(dwHeaps, pHeaps);

        for(INT i=0; i<dwHeaps; ++i)
        {
            VirtualQuery((LPVOID)pHeaps[i], &mi, sizeof(mi));
            
            if(pObject >= mi.BaseAddress 
                && (DWORD)pObject < (DWORD)mi.BaseAddress + mi.RegionSize)
            {
                bRet = TRUE;
                break;
            }
        }
    }
    delete []pHeaps;

    return bRet;
}

int g_value = 10;

int main(int argc, char* argv[])
{
    int nStackValue = 1;
    int* pNew = new int(10);
    int* pNewArray = new int[100];

    static int static_value = 0;
    
    cout << "g_value:" << IsObjectOnHeap(&g_value) << endl;  //false
    cout << "nStackValue:" << IsObjectOnHeap(&nStackValue) << endl;   //false
    cout << "static_value:" << IsObjectOnHeap(&static_value) << endl;    //false
    cout << "pNew:" << IsObjectOnHeap(pNew) << endl;        //true
    cout << "pNewArray:" << IsObjectOnHeap(pNewArray) << endl;    //true

    system("pause");
    return 0;
}

以上代码在Windows下测试通过(也只能在Windows上跑),如果有不正确的地方,欢迎指正。

注: 上面关于对象是否在堆上的判断应该是不对,因为堆内存不是连续内存,内部是通过类似链表的结构来实现的,<<软件调试>>里有相关介绍,也可以通过WinDbg的 !address 命令查看内存分布
posted on 2012-05-12 14:30 Richard Wei 阅读(5343) 评论(8)  编辑 收藏 引用 所属分类: C++

FeedBack:
# re: 如何判断一个C++对象是否在堆上
2012-05-12 22:57 | Duwen
其实判断一个对象是在堆上还是在栈上根本不必这么复杂,因为,系统默认栈地址是固定的,栈空间最多长到1MB,若超过1MB会引发栈耗尽异常,所以只需判断是否在栈上,就只是看看有没有在这个地址区间内  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-12 23:05 | Richard Wei
@Duwen
堆栈大小一般默认是1M,但是VC在编译时是可以通过/STACK :reserve[,commit] 设置的

另外对象所在位置, 除了堆(Heap)和栈(Stack),还有静态存储区等  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-13 08:12 | Duwen
是的,栈基址的却可以通过/STACK开关指定.我说的方法只是用于一些只有一个线程简单项目的判断,如果要对多线程,就得用你这个方法了,其实简单的判断单线程程序,我这个方发也是可行的,因为程序中如果是自己定义的变量只能在三个地方,全局区,栈区,堆,当然手工添加PE节区的情况除外,比如创建dll时加一个共享段,然后把变量定义在这个共享段中,全局区是存放静态数据和全局变量,所以如果我们要测试的不是静态变量或者全局变量,那么它不在堆区就在栈.至于静态存储区是个广义的说法,可以是全局区,栈,文字常量区,代码区.

对了,为了验证new 时c/c++ CLR会创建自己的堆我做了测试,发现一个有趣的事,就是在程序中,一开始,就有4个堆,我把所有标准C++文件全部移出,只留一个Windows.h, 并连接release版c/c++ CLR,结果还是4,我把windows.h移出结果也是四,即使我在我的Cpp中没有new,结果还是4,发现要想验证这个还真是不好办,因为头文件中库函数也可以new,也可以创建堆等等有太多不可控制因素,我能想到的就是看源码了,但我还真没找到源码,不知你有没有什么方法.
  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-13 08:28 | 13174115
那是不是要得到当前运行函数的地址
然后去比对地址?
  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-13 09:52 | Richard Wei
@Duwen
windows NT内核初始化时就有3个堆,且余留大小都是1MB,提交大小是8KB,其中一个是系统默认堆, 我们调用GlobalAlloc(),就是在默认堆里分配的内存。
C/C++ CRT会创建自己的堆,我们平时new对象,都是在该堆里分配的内存。所以我们一般看到的程序起码有4个堆。
另外如果你用了COM组件(调用了::CoInitialize(NULL)), 他也会建立自己的2个堆,CoTaskMemAlloc() 和 SysAllocString()都在在里面分配的内存。
一般写应用程序,理解到此就够了,再深入就要看Windows内核的源代码了。
  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-13 11:54 | anexx
@Duwen
这个不是windows 头文件的问题,而是c++ 运行时的问题。你如果把入口函数设置成main或winmain而不用编译器自动生成的,就不一样了。  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-05-13 21:33 | Duwen
@anexx,Richard wei
我在crt0.c中找到了__tmainCRTStartup的源码,找到了,
if ( !_heap_init(1) ) /* initialize heap */
fast_error_exit(_RT_HEAPINIT); /* write message and die */
其中_heap_init(1) 里有:
_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE,
BYTES_PER_PAGE, 0 )) == NULL )
而_crtheap是个全局句柄。

Richard wei说win NT 内核初始化时就有3个堆...,我想多了解点,于是今天去图书馆找了好几本书,都没有找到相关内容,不过由于时间关系,WRK里还没找,

  回复  更多评论
  
# re: 如何判断一个C++对象是否在堆上
2012-08-13 14:30 | Richard Wei
上面关于对象是否在堆上的判断应该是不对,因为堆内存不是连续内存,内部是通过类似链表的结构来实现的,<<软件调试>>里有相关介绍,也可以通过WinDbg的 !address 命令查看内存分布  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理