如何判断任一内存地址是堆上的还是栈上,若是堆上的返回该内存长度

2024-04-26 13:32

本文主要是介绍如何判断任一内存地址是堆上的还是栈上,若是堆上的返回该内存长度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

很早以前就想过这个问题:看到一个内存地址,如果判断这个地址是不是堆上的,若是,new出来的长度是多少字节?深入了解了new和delete的源码后,终于把这个方法找到了,在此分享给大家。

每个进程启动时候会有4G的虚拟内存,分为堆区、栈区、静态存储区、常量区、代码段、数据段和内核空间,而对每个线程,默认分配给其1MB空间。计算机一般采用的是小端模式存储,栈是向低地址生长,堆是向高地址生长。处于Ring3的应用程序是不可以访问内核空间(2G-64KB)的。

下面说一下new,新分配的内存前48字节为结构体_CrtMemBlockHeader(最后一个成员gap[4]=fdfdfdfd),后4字节为fdfdfdfd;这52字节时new的内存前后标示,若被破坏,则释放时会检查报错。于是可用下买呢方法来确定该地址是否在堆上,并计算出其长度:

//<<判断任一内存地址是否是堆内存,若是则返回内存长度,若不是返回-1

inline int CalcMemLen(void *pSrc, longfindRange = 10000)

{

         //crt头文件不能包含,这里定义下

         typedefstruct _CrtMemBlockHeader

         {

                   struct_CrtMemBlockHeader * pBlockHeaderNext;

                   struct_CrtMemBlockHeader * pBlockHeaderPrev;

                   char*                      szFileName;

                   int                         nLine;

#if defined (_WIN64)|| defined (WIN64)

                   int                         nBlockUse;

                   size_t                      nDataSize;

#else  /* _WIN64 */

                   size_t                      nDataSize;

                   int                         nBlockUse;

#endif  /* _WIN64 */

                   long                        lRequest;

                   unsignedchar               gap[4];

         } _CrtMemBlockHeader;

 

#define pbData(pblock) ((unsignedchar *)((_CrtMemBlockHeader *)pblock + 1))

#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)

#define _BLOCK_TYPE_IS_VALID(use) (_BLOCK_TYPE(use) ==_CLIENT_BLOCK || \

         (use) == _NORMAL_BLOCK || \

         _BLOCK_TYPE(use) == _CRT_BLOCK    || \

         (use) == _IGNORE_BLOCK)

 

         static unsigned char_bNoMansLandFill = 0xFD;

         unsignedchar *pc = (unsignedchar*)pSrc; //char*的话, -3不等于fd啊?

         _CrtMemBlockHeader *pHead = NULL;

         unsignedchar *pStart=NULL, *pEnd=NULL;

         inti=0;

 

         //找头部标示

         for(i=0; i<findRange; i++)

         {

                   if(pc[-i-4]==_bNoMansLandFill && pc[-i-3]==_bNoMansLandFill &&pc[-i-2]==_bNoMansLandFill && pc[-i-1]==_bNoMansLandFill)

                   {

                            pStart =&pc[-i];

                            pHead =pHdr(pStart);

                            break;

                   }

         }

 

         if(i>=findRange || !_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))

         {//指定范围内没有找到头部标示;或者找到了但不是有效的

                   return-1;

         }

 

         //找尾部标示

         for(i=0; i<findRange; i++)

         {

                   if(pc[i]==0xfd && pc[i+1]==0xfd && pc[i+2]==0xfd &&pc[i+3]==0xfd && pc[i+4] !=0xfd)//0xfd fd fdfd

                   {

                            pEnd = pc+i;

                            break;

                   }

         }

         if(i>=findRange)

         {//指定范围内没有找到尾部标示;

                   return-1;

         }

 

         intlength = int(pEnd - pStart);

         if(pHead->nDataSize == length)

         {

                   if(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))

                   {

                            if (_CrtIsValidHeapPointer(pStart))

                            {

                                     return pHead->nDataSize;

                            }

                   }

         }

 

         return-1;

}


下面在win64平台上测试一下:

class String

{

public:

         /*explicit*/String(const char*str=NULL);//普通构造函数

         String(constString &str);//拷贝构造函数

         String & operator=(const String &str);//赋值函数

         ~String();//析构函数

private:

public:

         char*m_data;//用于保存字符串

         char  m_c;

         unsignedshort  m_i;

};

ps的地址为0x208ee0,起始0-7个字节存的是指针m_data的值,win64指针是8字节,小端模式倒过来就是0x0000000000208f60;第8个字节存储m_c;由于sizeof(usingned short)=2,故m_i要对齐到2的整数倍也就是10了,故第10-11字节8eea-8eeb存储m_i;最后整个类按照pragma pack指定的值8min{类内最长元素(m_data8字节),机器cpu长度(8字节)}对齐,那就是把12补齐到16了,故sizeof(String)==16;如果类前加一句#pragma pack(push,1),则sizeof(String)==11;

由于构造函数中m_data=new char[1],故重新在堆上分配了内存,地址8f30-8f5f为其头部标示,8f61-8f64为其尾部标示。

测试结果:

                int *aa = new int[10];
int allLen = CalcMemLen(aa);//40

                int bb[1];
bb[2] = 9999;
allLen = CalcMemLen(bb);//-1

String *ps = new String;
allLen = CalcMemLen(ps);//16
  allLen = CalcMemLen(&ps->m_i);//16
allLen = CalcMemLen(&ps->m_data);//16
  allLen = CalcMemLen(ps->m_data);//1

char *pAppPath = NULL;
_get_pgmptr(&pAppPath);
allLen = CalcMemLen(pAppPath);//-1
delete pAppPath;//非堆内存,不可释放

2.变量一定要初始化,尤其是类和结构体的成员变量。一般地,对于win32上int变量i,在应用程序首次加载时:Debug模式下初始值为0xcccccccc,Release模式下初始值为0;但随着程序的运行,栈上的空间不断被该进程申请释放,下一次进入时,i可能是其他未预期的值。对于栈上的内存,无法计算其长度,数组越界访问了一般也不会报错(不过尽量保证不要越界,值得提醒一下,如果上面的bb定义在类String内,那么越界后就很容易破坏掉ps指针的边界,导致释放时报错)。




















这篇关于如何判断任一内存地址是堆上的还是栈上,若是堆上的返回该内存长度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/937711

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

【408DS算法题】039进阶-判断图中路径是否存在

Index 题目分析实现总结 题目 对于给定的图G,设计函数实现判断G中是否含有从start结点到stop结点的路径。 分析实现 对于图的路径的存在性判断,有两种做法:(本文的实现均基于邻接矩阵存储方式的图) 1.图的BFS BFS的思路相对比较直观——从起始结点出发进行层次遍历,遍历过程中遇到结点i就表示存在路径start->i,故只需判断每个结点i是否就是stop

linux 判断某个命令是否安装

linux 判断某个命令是否安装 if ! [ -x "$(command -v git)" ]; thenecho 'Error: git is not installed.' >&2exit 1fi

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少

JVM 常见异常及内存诊断

栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存。 栈帧过多导致栈内存溢出 下述示例代码,由于递归深度没有限制且没有设置出口,每次方法的调用都会产生一个栈帧导致了创建的栈帧过多,而导致内存溢出(StackOverflowError)。 示例代码: 运行结果: 栈帧过大导致栈内存

理解java虚拟机内存收集

学习《深入理解Java虚拟机》时个人的理解笔记 1、为什么要去了解垃圾收集和内存回收技术? 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些“自动化”的技术实施必要的监控和调节。 2、“哲学三问”内存收集 what?when?how? 那些内存需要回收?什么时候回收?如何回收? 这是一个整体的问题,确定了什么状态的内存可以