C++new和delete实现原理(汇编解释)

2023-05-16

new和delete最终调用malloc和free,关于malloc和free实现原理参见这篇文章:

http://blog.csdn.net/passion_wu128/article/details/38964045

new
new操作针对数据类型的处理,分为两种情况:
1,简单数据类型(包括基本数据类型和不需要构造函数的类型)
代码实例:
int* p = new int;
汇编码如下:
    int* p = new int;
00E54C44  push        4  
00E54C46  call        operator new (0E51384h)  
00E54C4B  add         esp,4  
分析:传入4byte的参数后调用operator new。其源码如下:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                        _THROW_NCEE(_XSTD bad_alloc, );
                }
 
        return (p);
        }
分析:调用malloc失败后会调用_callnewh。如果_callnewh返回0则抛出bac_alloc异常,返回非零则继续分配内存。
这个_callnewh是什么呢?它是一个new handler,通俗来讲就是new失败的时候调用的回调函数。可以通过_set_new_handler来设置。下面举个实例:
#include <stdio.h>
#include <new.h>
int MyNewHandler(size_t size)
{
    printf("Allocation failed.Try again");
    return 1;        //continue to allocate
    //return 0;        //stop allocating,throw bad_alloc
}
void main()
{
    // Set the failure handler for new to be MyNewHandler.
    _set_new_handler(MyNewHandler);
 
    while (1)
    {
        int* p = new int[10000000];
    }
}

在new基本数据类型的时候还可以指定初始化值,比如:
int* p = new int(4);

总结:
简单类型直接调用operator new分配内存;
可以通过new_handler来处理new失败的情况;
new分配失败的时候不像malloc那样返回NULL,它直接抛出异常。要判断是否分配成功应该用异常捕获的机制;

2,复杂数据类型(需要由构造函数初始化对象)

代码实例:
class Object
{
public:
    Object()
    {
        _val = 1;
    }
 
    ~Object()
    {
    }
private:
    int _val;
};
 
void main()
{
    Object* p = new Object();
}
汇编码如下:
    Object* p = new Object();
00AD7EDD  push        4  
00AD7EDF  call        operator new (0AD1384h)  
00AD7EE4  add         esp,4  
00AD7EE7  mov         dword ptr [ebp-0E0h],eax  
00AD7EED  mov         dword ptr [ebp-4],0  
00AD7EF4  cmp         dword ptr [ebp-0E0h],0  
00AD7EFB  je          main+70h (0AD7F10h)  
00AD7EFD  mov         ecx,dword ptr [ebp-0E0h]  
00AD7F03  call        Object::Object (0AD1433h)        //在new的地址上调用构造函数
00AD7F08  mov         dword ptr [ebp-0F4h],eax  
00AD7F0E  jmp         main+7Ah (0AD7F1Ah)  
00AD7F10  mov         dword ptr [ebp-0F4h],0  
00AD7F1A  mov         eax,dword ptr [ebp-0F4h]  
00AD7F20  mov         dword ptr [ebp-0ECh],eax  
00AD7F26  mov         dword ptr [ebp-4],0FFFFFFFFh  
00AD7F2D  mov         ecx,dword ptr [ebp-0ECh]  
00AD7F33  mov         dword ptr [p],ecx  
总结:
new 复杂数据类型的时候先调用operator new,然后在分配的内存上调用构造函数。
delete
delete也分为两种情况:
1,简单数据类型(包括基本数据类型和不需要析构函数的类型)。
int *p = new int(1);
delete p;
delete的汇编码如下:
    delete p;
00275314  mov         eax,dword ptr [p]  
00275317  mov         dword ptr [ebp-0D4h],eax  
0027531D  mov         ecx,dword ptr [ebp-0D4h]  
00275323  push        ecx  
00275324  call        operator delete (0271127h) 
分析:传入参数p之后调用operator delete,其源码如下:
void operator delete( void * p )
{
    RTCCALLBACK(_RTC_Free_hook, (p, 0));
 
    free( p );
}
RTCCALLBACK默认是空的宏定义,所以这个函数默认情况下就是简单的调用free函数。
总结:
delete简单数据类型默认只是调用free函数。

2,复杂数据类型(需要由析构函数销毁对象)
代码实例:
class Object
{
public:
    Object()
    {
        _val = 1;
    }
 
    ~Object()
    {
        cout << "destroy object" << endl;
    }
private:
    int _val;
};
 
void main()
{
    Object* p = new Object;
    delete p;
}
部分汇编码如下:
012241F0  mov         dword ptr [this],ecx  
012241F3  mov         ecx,dword ptr [this]  
012241F6  call        Object::~Object (0122111Dh)                          //先调用析构函数
012241FB  mov         eax,dword ptr [ebp+8]  
012241FE  and         eax,1  
01224201  je          Object::`scalar deleting destructor'+3Fh (0122420Fh)  
01224203  mov         eax,dword ptr [this]  
01224206  push        eax  
01224207  call        operator delete (01221145h)  
0122420C  add         esp,4 
总结:
delete复杂数据类型先调用析构函数再调用operator delete。
new数组
new[]也分为两种情况:
1,简单数据类型(包括基本数据类型和不需要析构函数的类型)。
new[] 调用的是operator new[],计算出数组总大小之后调用operator new。
值得一提的是,可以通过()初始化数组为零值,实例:
char* p = new char[32]();
等同于:
char *p = new char[32];
memset(p, 32, 0);
总结:
针对简单类型,new[]计算好大小后调用operator new。

2,复杂数据类型(需要由析构函数销毁对象)
实例:
class Object
{
public:
    Object()
    {
        _val = 1;
    }
 
    ~Object()
    {
        cout << "destroy object" << endl;
    }
private:
    int _val;
};
 
void main()
{
    Object* p = new Object[3];
}
new[]先调用operator new[]分配内存,然后在p的前四个字节写入数组大小,最后调用三次构造函数。
实际分配的内存块如下:

这里为什么要写入数组大小呢?因为对象析构时不得不用这个值,举个例子:
class Object
{
public:
    Object()
    {
        _val = 1;
    }
 
    virtual ~Object()
    {
        cout << "destroy Object" << endl;
    }
private:
    int _val;
};
 
class MyObject : public Object
{
public:
    ~MyObject()
    {
        cout << "destroy MyObject" << endl;
    }
private:
    int _foo;
};
 
void main()
{
    Object* p = new MyObject[3];
    delete[] p;
}
释放内存之前会调用每个对象的析构函数。但是编译器并不知道p实际所指对象的大小。如果没有储存数组大小,编译器如何知道该把p所指的内存分为几次来调用析构函数呢?
总结:
针对复杂类型,new[]会额外存储数组大小。
delete数组
delete[]也分为两种情况:
1,简单数据类型(包括基本数据类型和不需要析构函数的类型)。
delete和delete[]效果一样
比如下面的代码:
int* pint = new int[32];
delete pint;
 
char* pch = new char[32];
delete pch;
运行后不会有什么问题,内存也能完成的被释放。看下汇编码就知道operator delete[]就是简单的调用operator delete。
总结:
针对简单类型,delete和delete[]等同。

2,复杂数据类型(需要由析构函数销毁对象)
释放内存之前会先调用每个对象的析构函数。
new[]分配的内存只能由delete[]释放。如果由delete释放会崩溃,为什么会崩溃呢?
假设指针p指向new[]分配的内存。因为要4字节存储数组大小,实际分配的内存地址为[p-4],系统记录的也是这个地址。delete[]实际释放的就是p-4指向的内存。而delete会直接释放p指向的内存,这个内存根本没有被系统记录,所以会崩溃。
总结:
针对复杂类型,new[]出来的内存只能由delete[]释放。
原文:https://blog.csdn.net/passion_wu128/article/details/38966581 
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++new和delete实现原理(汇编解释) 的相关文章

  • android中多线程绘制曲线图实例

    画板基类继承view package com mytest myapplication Utility import android graphics Color import android util Log import android
  • InterlockedCompareExchangePointer 的作用和例子

    PVOID InterlockedCompareExchangePointer in out PVOID volatile Destination in PVOID Exchange in PVOID Comperand Interlock
  • java读取xml文件的四种方法

    Xml代码 1 lt xml version 61 34 1 0 34 encoding 61 34 GB2312 34 gt 2 lt RESULT gt 3 lt VALUE gt 4 lt NO gt A1234 lt NO gt 5
  • Android多线程之同步锁的使用

    本文主要介绍了Android多线程之同步锁的使用 xff0c 分享给大家 xff0c 具体如下 xff1a 一 同步机制关键字synchronized 对于Java来说 xff0c 最常用的同步机制就是synchronized关键字 xff
  • C#使用 System.Net.Mail发送邮件功能

    C 使用 System Net Mail发送邮件功能 NET 里包含了很多很丰富的邮件发送与接受的API在 System Net Mail命名空间里 xff0c 使得我们开发发送和接受邮件相关功能变得简单 xff0c 下面是一个简单发送邮件
  • C#SMTP发邮件

    简单邮件传输协议 Simple Mail Transfer Protocol SMTP 是事实上的在Internet传输email的标准 C 邮件传输主要用到了 SmtpClient 和MailMessage 类 SmtpClient类是客
  • C#发送邮件实例

    using System using System Collections Generic using System ComponentModel using System Data using System Drawing using S
  • closehandle()函数

    引用自 百度百科 xff0c 用于解决今天遇到的close handle 后什么时候释放资源问题 xff1a 方法名称 xff1a CloseHandle 位置 xff1a Kernel32 dll BOOL CloseHandle HAN
  • c++清空串口缓冲区

    缓冲区控制 Win32通信API除了提供SetupComm 函数实现初始化的缓冲区控制外 xff0c 还提供了PurgeComm 函数和FlushFileBuffers 函数来进行缓冲区操作 PurgeComm 函数的声明如下 xff1a
  • C++ int与string的转化

    int本身也要用一串字符表示 xff0c 前后没有双引号 xff0c 告诉编译器把它当作一个数解释 缺省情况下 xff0c 是当成10进制 xff08 dec xff09 来解释 xff0c 如果想用8进制 xff0c 16进制 xff0c
  • c++中字符数组内存和指针问题示例解答

    char id 61 34 123456 34 char c 61 34 SN 61 34 unsigned char buffer 20 int j 61 0 for int i 61 0 i lt strlen c i 43 43 bu
  • c++中LPCTSTR,LPTSTR 解释

    char是C语言标准数据类型 xff0c 字符型 xff0c 至于由几个字节组成通常由编译器决定 xff0c 一般一个字节 Windows为了消除各编译器的差别 xff0c 重新定义了一些数据类型 xff0c 你提到了另外几个类型都是这样
  • Delphi 自定义事件的例子

    我们这个控件将演示控件的自定义事件的书写 这个控件有一个类型为string的SensitiveText属性 xff0c 当用户在输入框中输入的文字为InvalidText时就会触发OnSensitiveText事件 按照惯例 xff0c 我
  • c++中sscanf的用法

    sscanf 读取格式化的字符串中的数据 swscanf 是 sscanf 的宽字符版本 xff1b swscanf 的参数是宽字符串 swscanf不处理 Unicode 全角十六进制或 34 兼容性区 34 字符 除此以外 xff0c
  • c++内存测试

    void MemoryTest 内存测试 指针嵌套 char rr 栈中分配内存 系统自动分配释放 xff09 int ee 61 int amp rr 将rr的内存地址转换成整型数 char yy 61 amp rr 定义一个字符型指针y
  • C/C++串口通信原理及读写与操作

    http wangbaiyuan cn c serial communication write reading html 展开 文章目录 在工业控制中 xff0c 工控机 xff08 一般都基于Windows平台 xff09 经常需要与智
  • c# 调用c库dll ,char*转string的解决办法

    最近由于有个未知的设备需要用到modbus通讯协议 xff0c 底层需要与PLC通讯 xff0c 坤跌 xff0c PLC啥型号也不清楚封在里面不能拆 前人只留了几个不能运行的QT代码以及不完整的文档 用惯了C 想要重新学QT xff0c
  • C++多线程编程(入门实例)

    多线程在编程中有相当重要的地位 xff0c 我们在实际开发时或者找工作面试时总能遇到多线程的问题 xff0c 对多线程的理解程度从一个侧面反映了程序员的编程水平 其实C 43 43 语言本身并没有提供多线程机制 xff08 当然目前C 43
  • Android Studio 使用Log

    Android使用log来记录信息 xff0c 测试了下 xff0c 和system out println区别不大 xff0c 主要优势在于能使用过滤器过滤日志 本文记录基础的log使用方法 xff0c 来自 第一行代码 xff0c 以及
  • 指针强制转换问题

    void ff void abc 任意类型数据指针 xff08 指针即内存地址 xff09 int z 61 int abc 强制转换成int 指针变量 int zz 61 z 获取内存中的值

随机推荐

  • 新手git教程

    本文转载自 xff1a http igeekbar com igeekbar post 82 htm Git近些年的火爆程度非同一般 xff0c 这个版本控制系统被广泛地用在大型开源项目 xff08 比如Linux xff09 xff0c
  • 使用Project进行项目管理

    下面开始介绍Project的使用 1 从下列地址获取Project 2010的副本 版权问题 xff0c 已删除地址 2 安装 2 1 版权页 2 2 自定义安装页 2 3 安装完毕 3 使用该软件进行项目管理 3 1 打开Project
  • Marshal在C#中的应用(void *指针到IntPtr的转化)

    C 调用C语言的API时一般把void 指针转换成IntPtr xff0c 但这经常远远不够的 在C语言中void 是个万金油 xff0c 尤其是一些老的c语言程序 xff0c 所有的参数就一个void 指针 xff0c 里面包罗万象 xf
  • VS2012 2013 无法显示查找功能 无法具体定位 解决方法

    xfeff xfeff 问题的现象 通过使用 Ctrl 43 Shift 43 F 也就是Find In Files功能 xff0c 使用之后只能显示统计结果 不显示具体行 如下图 regedit 中在注册表中查找 xff1a HKEY C
  • C#中使用指针转换数据类型[C#/unsafe]

    今日因为一个同事说起 xff0c 在原来的旧系统中使用指针做数据转换很方便 xff0c 比如要把浮点数转化为数组 xff0c 也或者是字符串的相互转换 xff1b 当然 xff0c 大家都知道c 中实现指针只需要写入unsafe 编译选项把
  • c#指针的使用例程

    unsafe double value 61 888888 byte v1 61 BitConverter GetBytes value byte v2 61 new byte v1 Length double pv 61 amp valu
  • MPAndroidChart LineChart 折线图 你要的都在这里了

    前言 MPAndroidChart已经出了很长的一段时间 xff0c 相信大家也有所耳闻 xff0c 自己也使用了有一段时间 xff0c 固在此写下文章 xff0c 根据项目的需求 xff0c 记录一些见解与问题 xff0c 作为参考 望大
  • Android Chart框架 MPAndroidChart 坐标轴设置

    1 轴线的绘制 设置轴线就先必须取得轴线类Axis 在一个图标中有三个轴线 xff1a x轴 xff1a 调用 getXAxis 获取左边y轴 xff1a 调用 getAxisLeft 获取右边y轴 xff1a 调用 getAxisRigh
  • Android图表控件MPAndroidChart——曲线图LineChart的使用(财富收益图)

    目录 前言 本文涉及文章 其他相关文章 1 数据准备 1 1 数据来源 2 曲线展示 2 1 MPAndroidChart获取 2 2 数据对象获取 2 3 数据展示 3 曲线完善 3 1 图表背景 边框 网格线修改 3 2 X Y轴值的自
  • Android资源文件在配置文件中的使用

    Android资源文件大致可以分为两种 xff1a 第一种是res目录下存放的可编译的资源文件 xff1a 这种资源文件系统会在R Java里面自动生成该资源文件的ID xff0c 所以访问这种资源文件比较简单 xff0c 通过R XXX
  • android 如何创建配置文件和读配置文件

    因为一些配置信息 xff0c 多处用到的 且以后可能变更的 xff0c 我想写个 prorperties配置文件给管理起来 在studio中新建一个Assets文件 gt 新建一个file文件类型为properties文件 该文件可以与re
  • android上如何写配置文件

    android上如何写配置文件 xff1a 使用SharedPreferences SharedPreferences是Android平台上一个轻量级的存储类 xff0c 用来保存应用的一些常用配置 xff0c 比如Activity状态 x
  • android SharedPreferences的用法

    之前做应用时碰到这样一个问题 xff1a 在 A Activity 要与 B Activity的Fragment进行通信传值 xff0c 但是忽然发现无法拿到B中Fragment的Handler xff0c 又不能发送广播 xff0c 短暂
  • Android中数据库的一些操作(增删改查)

    提起Android的开发 xff0c 就不得不提数据库 xff0c 几乎每个App中都会用到Sqlit数据库存储一些数据 xff0c 小编闲暇时期 xff0c 写了一个小demo关于数据库的增删改查 xff0c 之前也介绍过数据库的一个开源
  • VC2008 无法调试,无法断点,断点无效的最终解决方法

    今天VC2008忽然又出现断点无效了 按上次博客说的格式化了源文件 也删了编译工程数据库文件 问题依就 经过测试找到了终解决方法 其实出现这个问题常常出现在工程中某一个源文件中 多是文件内的一些不可视符出了问题 比较多的是由于制表符Tab
  • 计蒜客 蓝桥杯模拟赛1 马的管辖

    代码来自https blog csdn net weixin 41793113 article details 86721181 在中国象棋中 xff0c 马是走日字的 一个马的管辖范围指的是当前位置以及一步之内能走到的位置 xff0c 下
  • 菜单项onCreateOptionsMenu()和onOptionsItemSelected()的使用

    函数onCreateOptionsMenu 为创建Menu菜单的项目 函数onOptionsItemSelected 为处理菜单被选中运行后的事件处理 首先看下Activity的内容 xff1a lt span style 61 34 fo
  • C# 解决窗体假死的状态 非常有用!且非常重要!

    异步调用是CLR为开发者提供的一种重要的编程手段 xff0c 它也是构建高性能 可伸缩应用程序的关键 在多核CPU越来越普及的今天 xff0c 异步编程允许使用非常少的线程执行很多操作 我们通常使用异步完成许多计算型 IO型的复杂 耗时操作
  • C# Message 消息处理

    一 消息概述 Windows下应用程序的执行是通过消息驱动的 消息是整个应用程序的工作引擎 xff0c 我们需要理解掌握我们使用的编程语言是如何封装消息的原理 C 自定义消息通信往往采用事件驱动的方式实现 xff0c 但有时候我们不得不采用
  • C++new和delete实现原理(汇编解释)

    new和delete最终调用malloc和free xff0c 关于malloc和free实现原理参见这篇文章 xff1a http blog csdn net passion wu128 article details 38964045