malloc、realloc和new你明白了么?

本文探讨了malloc、realloc和new在动态内存分配中的应用差异,重点讲解了它们的使用场景及注意事项,并通过实例对比了不同情况下代码实现的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//========================================================================
//TITLE:
//    malloc、realloc和new你明白了么?
//AUTHOR:
//    norains
//DATE:
//    Saturday  05-January-2008
//Environment:
//    VS2005 + SDK-WINCE5.0-MIPSII  
//    EVC + SDK-WINCE5.0-MIPSII  
//========================================================================
    malloc和realloc是C标准函数,而new是C++的关键字,三者都能动态分配内存.但其中一些有意思的细节,你注意了么?
   
    在开篇之前,我们首先需要明确一个概念,数据存储在内存中是无针对性的,换句话说,内存里面的数据,究竟是解释为字符串还是解释为整数,甚至是一个DWORD值还是一个拥有两个WORD的数值,它都是不知道的.如何解释内存中的数据,只和指向这段内存的指针有关.
   
    我们可以用以下代码验证:
 

int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    
int       nCmdShow)
{
#define MAX_SIZE_MALLOC            8

    BYTE 
*pBuf = (BYTE *)malloc(MAX_SIZE_MALLOC);
    memset(pBuf 
+ 0,0x17,1);
    memset(pBuf 
+ 1,0x4f,1);
    memset(pBuf 
+ 2,0xBA,1);
    memset(pBuf 
+ 3,0x4E,1);
    memset(pBuf 
+ 4,0x9B,1);
    memset(pBuf 
+ 5,0x52,1);
    memset(pBuf 
+ 6,0x00,1);
    memset(pBuf 
+ 7,0x00,1);

    WCHAR 
*pStr = (WCHAR *) pBuf;
    wprintf(L
"Wide-String : %s ",pStr);


    
int *pInt = (int *)pBuf;
    
for(int i = 0 ; i < MAX_SIZE_MALLOC / sizeof(int); i ++)
    {        
        printf(
"pInt[%d]: %d ",i,pInt[i]);        
    }

    free(pBuf);

    Sleep(
1000);
}


    输出结果如下:
    Wide-String : 众人力
    pInt[0]: 1320832791
    pInt[1]: 21147

   
    同样的的内存,只是因为指向的指针类型不同,所输出的结果迥然各异:因为pStr为WCHAR指针,所以将其解释为字符串,输出"众人力";而pInt为int类型指针,而指向的内存大小恰好等于两个int空间,因此又可以采用数组下标的方式获取数值.
       
    了解内存储存的无针对性之后,下面的内容就简单多了.
   
   
    首先我们需要知道内存空间的计量单位.一般来说,内存分配函数都是以BYTE(字节)为计量单位,如果我们分配两个BYTE的内存,代码则将是如此:

    PVOID pBuf = malloc(2);
   
    知道这个,下面所要说到的例子就好理解了.
   
    假设我们有个WCHAR数组,我们需要在使用之前将其全部置0,如果是静态分配,那则是非常简单:
    WCHAR pszNew[MAX_COUNT] = {0};

   
    但如果是用new分配的呢?因为用new动态分配无法初始化,所以我们需要手动置0,可能很多朋友的第一想法是如此:
    for(int i = 0; i < MAX_COUNT; i ++)
    {
      pszNew[i] 
= 0;
    }

    如果MAX_COUT比较大,那估计耗费在置0的时间上不是小数.其实,我们完全可以采用memset替代这个循环.而用memset最重要的一点是,我们需要知道pszNew新分配的数组占了多大的内存空间.不过,这也非常简单.一个WCHAR占据的内存为sizeof(WCHAR),则MAX_COUNT个为MAX_COUNT * sizeof(WCHAR).所以上面的代码可以用memset改写成:
    memset(pszNew,0,MAX_COUNT * sizeof(WCHAR));

    理解这个内存存储空间后,我们也不难明白将pszNew写到文件中的代码为何如此了:
    WriteFile(hFile,pszNew,MAX_COUNT * sizeof(WCHAR),&dwWritten, NULL);

    malloc和new都可以动态分配内存,那在实际中如何取舍?一般来说,如果空间是一次性分配,并且不需要再对已分配内存再次进行大小分配,推荐使用new,否则就应该用malloc.
   
    举个例子比较容易理解.假如我们需要分配一个动态内存保存字符串pszA,然后不久之后又有一个字符串pszB送来,而这时需要在原有的内存基础上再追加pszB.如果采用new分配内存,则代码可能会如此:
        
    ...

    
//这代码段用来存储pszA
    
//pBuf指向存储的地址
    TCHAR *pBuf = new TCHAR[_tcslen(pszA) + 1];
    _tcscpy(pBuf,pszA);  
    
    ...  

    
//下面代码追加pszB
    TCHAR *pNew = new TCHAR[_tcslen(pBuf) + _tcslen(pszB) + 1];
    _tcscpy(pNew,pBuf);
    _tcscat(pNew,pszB);
    delete [] pBuf;
    pBuf 
= pNew;

    如果pBuf内存是用malloc分配的,因为可以使用realloc在保持原有数据的基础上重新分配内存,故代码显得更为精炼:
    ...
    
    
//这代码段用来存储pszA
    TCHAR *pBuf = (TCHAR *)malloc((_tcslen(pszA) + 1* sizeof(TCHAR));
    _tcscpy(pBuf,pszA);

    ...
    
    
//下面代码追加pszB
    pBuf = realloc(pBuf,(_tcslen(pBuf) + _tcslen(pszB) + 1* sizeof(TCHAR));
    _tcscat(pBuf,pszB);


    既然realloc这么方便,那么可不可以先用new分配内存,然后再用realloc重分配呢?最好不要如此.这点在MSDN中描述得很清楚,如果用new分配的内存只能用delete,相对malloc和realloc也只能用free.那如果用free释放new分配的内存会是一种什么结果呢?C++标准中关于这个行为未定义,也就是说,可能没问题,但也不排除出现异常的可能.所以,我们还是老老实实按照建议行事吧.
   
    虽然接下来的内容看似主题无关,但毕竟还是和内存有千丝万缕的联系,所以在这也多说两句,就当成是文章的结尾吧.
   
    给一个变量赋值,给我们的第一直觉是采用"=",这也是最常用的方法,如果想比较BT,你还可以采用memset.就以文章开头的代码做例子为承接而结束全文吧:
    //以下每段代码都等价:
    *pBuf = 0x17;
    pBuf[
0= 0x17;
    memset(pBuf 
+ 0,0x17,1);
### C++ 中 `malloc`、`realloc` `new` 的区别及使用方法 #### 动态内存分配概述 动态内存分配允许程序在运行时请求额外的存储空间。C++ 支持多种方式来实现这一点,其中最常用的是通过标准库函数 `malloc`/`realloc` 以及运算符 `new`。 #### 函数声明头文件引入 为了使用这些功能,在源代码顶部需包含相应的头文件: 对于 C 风格的内存管理操作 (`malloc`, `realloc`) ,应加入 `<cstdlib>` 或者更传统的 `<stdlib.h>` 。而针对面向对象特性的 `new` 运算符,则不需要显式导入任何特定头部单元,因为这是内置支持的一部分[^2]。 #### Malloc 使用说明 `malloc()` 是用于从堆上分配指定字节数量未初始化的空间,并返回指向该区域起始位置的一个通用指针(即 `void*`)。由于其不执行构造函数调用,因此仅适用于基本数据类型的简单数组创建场景。需要注意的是,当不再需要这块资源时务必记得调用 `free()` 来回收它所占用的位置,防止发生泄漏问题[^1]。 ```cpp // 假设要为整数数组预留足够的容量 int n = 5; int *array = static_cast<int*>(malloc(n * sizeof(int))); if (!array) { // 错误处理... } for (int i = 0; i < n; ++i){ array[i]=i*i; } free(array); ``` #### Realloc 使用说明 如果先前已经利用 `malloc()` 获取了一片连续地址区间但后来发现不够用了怎么办?这时就可以借助于 `realloc()` 实现原地扩展或收缩的目的——传入旧有缓冲区首址加上新的期望大小作为参数即可完成调整动作;当然也有可能因种种原因无法满足需求从而返回 NULL 指向新分配出来的更大范围内的另一处空白地带供后续迁移过去继续沿用下去。 ```cpp // 尝试增加原有数组长度至两倍 n *= 2; array = static_cast<int*>(realloc(array, n * sizeof(int))); if (!array) { // 错误处理... } else { for (; old_n < n; ++old_n){ array[old_n]=(old_n-old_n%3)*7+9; } } ``` #### New 使用说明 不同于上述两者基于过程式的编程范式设计思路,现代 C++ 更推崇采用类成员形式的新建实例语法糖衣包裹下的底层机制运作原理:每当遇到像 T obj[] 这样的表达式时编译器便会自动帮我们合成一段隐含着适当规模申请逻辑在内的机器码片段并负责安排好一切必要的准备工作直至最终能够顺利产出预期的对象实体出来为止。更重要的一点在于每当我们运用此关键字构建复合结构体的时候还会顺带触发关联模板定义里预置好的默认构造子例程去填充各个字段初始状态值确保不会残留不确定因素影响正常使用体验。 ```cpp class MyClass {}; MyClass* pInstance = new MyClass(); // 单个对象 delete pInstance; pInstance = new MyClass[10]; // 数组版本 delete [] pInstance; ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值