前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++|内存管理|new cookie实验

C++|内存管理|new cookie实验

作者头像
朝闻君
发布2021-11-22 11:07:48
5720
发布2021-11-22 11:07:48
举报

相关问题:

本文章来自于多年(误)前自己做的一次实验,因为打算什么时候把问题关了,故迁移实验内容自此。

实验原因

第一个问题的回答中,原答主指出:

代码语言:javascript
复制
    __inline void* _MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void* _Ptr, unsigned int _Marker)
    {
        if (_Ptr)
        {
            *((unsigned int*)_Ptr) = _Marker;
            _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
        }
        return _Ptr;
    }

malloc.h这个函数中存储了内存的大小,但是评论区有人指出实验结果不符,我个人也尝试了进行实验。此前我在Effective C++中了解了这个new cookie机制,但是未曾亲自动手。

预实验

通过对内存的打印,我在我创建的int堆数组附近并未找到类似的魔术数字。我仔细寻找了这个函数的访问点,唯一的访问点就在malloc.h内部。

代码语言:javascript
复制
#ifdef _DEBUG

    #ifndef _CRTDBG_MAP_ALLOC
        #undef _malloca
        #define _malloca(size)                                                           \
            __pragma(warning(suppress: 6255 6386))                                       \
            (_MallocaComputeSize(size) != 0                                              \
                ? _MarkAllocaS(malloc(_MallocaComputeSize(size)), _ALLOCA_S_HEAP_MARKER) \
                : NULL)
    #endif
#else

    #undef _malloca
    #define _malloca(size)                                                                 \
        __pragma(warning(suppress: 6255 6386))                                             \
        (_MallocaComputeSize(size) != 0                                                    \
            ? (((_MallocaComputeSize(size) <= _ALLOCA_S_THRESHOLD)                         \
                ? _MarkAllocaS(_alloca(_MallocaComputeSize(size)), _ALLOCA_S_STACK_MARKER) \
                : _MarkAllocaS(malloc(_MallocaComputeSize(size)), _ALLOCA_S_HEAP_MARKER))) \
            : NULL)

#endif

大意就是Debug模式下,所有_malloca(是微软的malloca私货,不是标准的malloc,所以原答主找错地方了)的内存均在堆上,而Release模式下,小数组的内存会通过_alloca而放到栈上。

代码语言:javascript
复制
    #define _ALLOCA_S_THRESHOLD     1024
    #define _ALLOCA_S_STACK_MARKER  0xCCCC
    #define _ALLOCA_S_HEAP_MARKER   0xDDDD

通过上述代码,我们可以很明显地发现,_MarkAllocaS函数的Marker参数并不是原答主回答的size,而仅仅是用于标记Stack和Heap的标识符,在超过1kb时分配的内存会在堆上。而原答主拥有27赞8评论,其中质疑者包括我在内只有两人。可以看出尽管很多人知道这个知识点,但具体实操却没有经历。

在多次探索char数组的内存而不得后,我通过遍历的方式撞击前后1kb的内存,看是否有魔术数字正好能对应内存大小,尽管内存中因为随机性有这样的数字,但是位置的规律性根本看不出。我查阅了资料得知malloc的大小应该存储在某些被设置为保护的区块头,于是宣告对char数组的内存存储实验以失败告终。

正式实验

在继续查阅了其他资料之后,我得知了new的机制和malloc存在差异,例如malloc/free本身只需要宣告内存被占用/释放即可,而new/delete却要完成对应的构造/析构操作,如果仅仅存储字节大小,由于内存对齐原因,内存长度 / sizeof(T) >= 对象个数,因此执行数组操作必须存储对象的数目而不仅仅是字节大小。

得到这个结论之后,很容易与

中的POD(即平凡构造/平凡析构)联系在一起。

C语言中不存在new,因此仿C的对象也不会有独特的new cookie机制。因此上述C风格的char数组,struct数组,以及POD(Plain Ol' Data)都不会存储这样的数组大小,自然也就没法找到了。

实验结果

在使用class封装int并添加上构造/析构函数后,我又一次对INT[32]探测了内存。结果如下

代码语言:javascript
复制
数组索引 地址 值
-2 000001D0B18F51C0 20
-1 000001D0B18F51C4 0

在数组前8-4字节处正确存放了数组的大小0X20,因此编译器能够在new/delete[]时正确析构对象了。

实验结论

  1. 对于栈中的自动对象,int a[5]等,直接由编译器提供大小,作为一种立即数直接参与汇编码中,这也是为什么栈数组必须使用常数的缘故,因为作为代码的一部分这必须是编译期间已知的。
  2. 对于堆上的内置类型或POD结构体(int,char等等),不存储大小,因为编译器根本无需析构,也没有必要知道数组具体的大小。内存的释放由malloc/free存储的字节大小处理即可。
  3. 对于堆上有构造或者析构函数的对象,在分配的对象前一段内存处分配size_t的大小存储大小,这段代码称为new cookie
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实验原因
  • 预实验
  • 正式实验
  • 实验结果
  • 实验结论
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档