w vs malloc

C++内存管理
 

1、new 是c++中的操作符,malloc是c 中的一个函数 2、new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数 3、内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。 4、new 和 malloc效率比较

new可以认为是malloc加构造函数的执行。

new出来的指针是直接带类型信息的。

而malloc返回的都是void指针。

一:new delete 是运算符,malloc,free是函数

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见示例。

 

class Obj

{

public :

          Obj(void){ cout << “Initialization” << endl; }

~Obj(void){ cout << “Destroy” << endl; }

void      Initialize(void){ cout << “Initialization” << endl; }

void      Destroy(void){ cout << “Destroy” << endl; }

};

 

void UseMallocFree(void)

{

      Obj    *a = (obj *)malloc(sizeof(obj));     // 申请动态内存

      a->Initialize();                          // 初始化

      //…

      a->Destroy();     // 清除工作

      free(a);          // 释放内存

}

 

void UseNewDelete(void)

{

      Obj    *a = new Obj;    // 申请动态内存并且初始化

      //…

      delete a;             // 清除并且释放内存

}

示例用malloc/free和new/delete如何实现对象的动态内存管理

类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。函数UseNewDelete则简单得多。

所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。

既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。

如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

二:new delete在实现上其实调用了malloc,free函数。

三:new operator除了分配内存,还要调用构造函数。

malloc函数只是负责分配内存。

 

1. malloc是库函数,在C++程序中,要用到malloc函数,必须包含malloc.h。malloc函数的原型是:void *malloc( size_t size ),其中,size是分配的空间大小。该函数返回一个void类型的指针,因此,在用malloc函数时,往往需要进行数据类型的强制转换:

              int *pm = (int*)malloc(2);

             myclass *pO1 = (myclass *)malloc(2); //myclass是一个自定义的类

       new是C++的一个运算符,一般用法如下:

int *p = new int[10];

              myclass *pO = new myclass;

2. 在分配空间时,malloc只是分配了指定大小的空间,并没有初始化,没有调用类的构造函数。而new不仅分配了空间,同时进行了初始化工作,调用了该对象的构造函数。

如下面的一个例程所示:

#include <iostream.h>

#include <malloc.h>

 

class myclass

 

{

public:

    myclass();

    void myfoo();

private:

    int m_nindex;

};

 

myclass::myclass()

 

{

    m_nindex = 1;

}

void myclass::myfoo()

 

{

    cout << " myfoo : m_nindex = " << m_nindex << endl;

}

 

int main()

 

{

    int *p = new int[10];

    myclass *pO = new myclass;

    myclass *pO1 = (myclass *)malloc(2);

 

    for(int i =0; i<10; i++)

 

    {

        p[i] = i;

        cout << p[i] << "    ";

    }

 

    cout << endl;

    pO->myfoo();

 

    pO1[0].myfoo();

    pO1[1].myfoo();

 

    delete[] p;

    delete pO;

    free(pO1);

 

    return 0;

}

 

new调用了类myclass的构造函数,而malloc只是分配了空间,并没有调用构造函数,因此会出现调用myfoo函数时,输出的m_nindex结果具有随机性。也可以通过断点调试观察发现,new调用了构造函数而malloc没有. 

另外注意,由于mallocnew都是在堆上分配的空间,无法自动释放,需要调用free/delete释放空间。malloc分配的空间用free释放,而new分配的空间用delete释放。

<think>我们正在讨论fprintf和printf的内存分配行为。用户想知道fprintf是否涉及动态内存分配(malloc),以及它与printf在内存分配上的差异。根据之前的讨论,我们知道printf在某些情况下会调用malloc,那么fprintf呢? 首先,fprintf和printf都属于C标准库的I/O函数,它们共享相同的底层实现机制。在glibc中,两者最终都会调用到vprintf内部函数。因此,fprintf在内存分配行为上与printf类似,但有一些关键差异: 1. **动态宽度/精度处理**: - 当使用`%*s`、`%*d`等动态格式说明符时,fprintf和printf都需要临时分配内存来处理可变宽度/精度参数。这是因为系统需要为可能很长的格式化结果准备缓冲区。 2. **缓冲区管理差异**: - printf默认输出到stdout,而stdout通常是行缓冲的(当连接到终端时) - fprintf可以输出到任意文件流,包括无缓冲或全缓冲的文件 - 对于缓冲的文件流,fprintf可能使用更大的缓冲区(例如通过setvbuf设置的缓冲区),但缓冲区本身通常是通过malloc分配的 3. **本地化处理**: - 当使用本地化相关格式(如`%ls`宽字符或货币格式)时,两者都可能调用malloc来创建转换缓冲区 4. **长字符串处理**: - 输出超过内部缓冲区大小的字符串时(通常为1KB),两者都会触发malloc分配临时缓冲区 **关键区别**: - **缓冲机制**:fprintf输出到文件时,如果文件流使用用户提供的缓冲区(通过setvbuf),则可能避免malloc调用;而printf总是使用内部缓冲区(可能动态分配) - **错误处理**:fprintf在写入文件失败时不会立即刷新缓冲区,可能减少临时分配 - **性能影响**:向已满缓冲区写入时,fprintf可能因刷新缓冲区而触发系统调用,但不会直接导致malloc **验证方法**: 1. **使用strace跟踪系统调用**: ```bash strace -e brk,mmap ./program_using_fprintf 2>&1 | grep "=0" ``` 2. **自定义malloc钩子**: ```c void* (*old_malloc)(size_t) = NULL; void* my_malloc(size_t size) { printf("malloc(%zu) called\n", size); return old_malloc ? old_malloc(size) : NULL; } int main() { old_malloc = __malloc_hook; __malloc_hook = my_malloc; fprintf(stderr, "Test: %*s\n", 1000, "buffer"); __malloc_hook = old_malloc; } ``` **安全建议**: 1. 避免动态格式说明符: ```c // 不安全 fprintf(file, "%*s", dynamic_width, str); // 安全替代 char buf[FIXED_SIZE]; snprintf(buf, sizeof(buf), "%.*s", (int)sizeof(buf)-1, str); fwrite(buf, strlen(buf), 1, file); ``` 2. 使用静态缓冲区: ```c #define MAX_LINE 1024 static char line_buf[MAX_LINE]; setvbuf(file, line_buf, _IOFBF, MAX_LINE); // 使用静态缓冲区 ``` **内存分配对比表**: | 场景 | printf | fprintf | |---------------------|--------|---------| | 常规短输出 | 无 | 无 | | 动态宽度(`%*s`) | 有 | 有 | | 本地化数字格式 | 有 | 有 | | 长字符串(>4KB) | 有 | 有 | | 浮点复杂格式 | 有 | 有 | | 使用用户缓冲区 | 不适用 | 可避免 | **结论**: fprintf与printf在内存分配行为上基本一致,但通过精心控制输出格式和使用setvbuf设置静态缓冲区,可以显著减少fprintf的malloc调用[^1][^4]。在内存严格受限的系统中,建议: - 完全避免动态格式说明符 - 限制单次输出长度 - 预分配文件缓冲区 - 考虑使用write等系统调用替代 --- ### 相关问题 1. 如何检测C标准库函数中的隐藏内存分配? 2. 在实时系统中如何实现完全无动态分配的日志输出? 3. setvbuf函数如何帮助减少内存分配?其内部机制是什么? 4. 除了printf/fprintf,还有哪些常用标准库函数会隐式调用malloc? 5. 如何修改GCC链接选项使程序完全禁用堆内存分配? [^1]: 动态内存分配原理 [^4]: Glibc内存安全机制</think>### fprintf 的内存分配行为分析 #### 1. **fprintf 是否会触发 malloc 调用?** **是的,fprintf 在某些场景下会触发 malloc 调用**,其行为与 printf 高度相似,因为它们共享相同的底层格式化引擎(如 glibc 的 `vfprintf` 实现)。以下情况可能触发动态内存分配: | 场景 | 触发原因 | 示例 | |-------------------------------|--------------------------------------------------------------------------|------------------------| | **动态宽度/精度参数** | 解析 `%*s`, `%*d` 等格式符需临时分配内存保存动态参数 | `fprintf(fp, "%*s", w, str)` | | **超长字符串输出** | 输出内容超过内部缓冲区大小(通常 1-4KB)需分配临时缓冲区 | 输出 >4KB 的字符串 | | **本地化/多字节字符处理** | 处理宽字符或本地化数字格式需额外转换缓冲区 | `fprintf(fp, "%ls", wstr)` | | **复杂浮点数格式化** | `%a`, `%g` 等格式可能因精度计算分配内存 | `fprintf(fp, "%a", 3.14)` | | **缓冲池扩展** | 当文件流缓冲区不足且未预分配静态缓冲区时 | 未调用 `setvbuf` 的情况 | #### 2. **fprintf 与 printf 的内存分配差异** 虽然两者核心逻辑相同,但存在关键区别: | 特性 | printf (stdout) | fprintf (任意文件流) | |---------------------|------------------------------------------|-------------------------------------| | **默认缓冲机制** | 行缓冲(终端)或全缓冲(重定向) | 取决于文件类型(全缓冲/无缓冲) | | **缓冲区来源** | 可能使用动态分配的默认缓冲区 | 可通过 `setvbuf` 使用静态预分配缓冲区 | | **错误处理影响** | 失败时可能遗留动态分配缓冲区 | 相同风险 | | **规避分配的能力** | 较弱(无法控制 stdout 缓冲) | 较强(可完全控制文件流缓冲) | #### 3. **避免 malloc 的解决方案** ##### 方法 1:禁用动态格式符(推荐) ```c // ✅ 安全:固定宽度 fprintf(fp, "%-10s %05d\n", "ID", 123); // ❌ 危险:动态宽度(可能触发 malloc) int width = 1000; fprintf(fp, "%*s", width, "data"); ``` ##### 方法 2:使用静态缓冲区 + `setvbuf` ```c FILE *fp = fopen("log.txt", "w"); char static_buf[4096]; // 预分配静态缓冲区 // 绑定静态缓冲区到文件流 setvbuf(fp, static_buf, _IOFBF, sizeof(static_buf)); // 完全缓冲模式 fprintf(fp, "Safe output without malloc"); // 不会触发动态分配 fclose(fp); ``` **原理**:`setvbuf` 将预分配缓冲区关联到文件流,避免 I/O 库内部动态分配[^4]。 ##### 方法 3:分块输出 + 长度控制 ```c const char *long_str = "..."; // >4KB 的字符串 size_t chunk_size = 1024; for (size_t i = 0; i < strlen(long_str); i += chunk_size) { // 每次写入固定长度块 fwrite(long_str + i, 1, min(chunk_size, strlen(long_str)-i), fp); } ``` 避免单次大尺寸格式化触发的分配。 #### 4. **关键验证实验** ```c #include <malloc.h> #include <stdio.h> // 自定义 malloc 拦截器 void* malloc_hook(size_t size, const void *caller) { fprintf(stderr, "[Malloc] Size: %zu, Caller: %p\n", size, caller); return NULL; // 返回 NULL 将导致格式化失败 } int main() { FILE *fp = fopen("test.txt", "w"); __malloc_hook = (void*(*)(size_t, const void*))malloc_hook; // 测试用例:动态宽度触发 malloc fprintf(fp, "%*s", 10000, "trigger"); // 此处将打印拦截日志 __malloc_hook = NULL; fclose(fp); return 0; } ``` 运行结果将显示: ``` [Malloc] Size: 10000, Caller: 0x7ffff7e3a5d0 # vfprintf 内部调用 ``` #### 5. **安全实践总结** | 场景 | 推荐方案 | 内存分配风险 | |--------------------|-----------------------------------|--------------| | 常规输出 | 避免动态格式符 | 零风险 | | 大文件日志 | `setvbuf` + 静态缓冲区 | 零风险 | | 不可控动态内容输出 | 分块 `fwrite` + 手动格式化 | 低风险 | | 实时系统 | 完全禁用动态格式 + 替代输出函数 | 零风险 | > **核心结论**:fprintf 的内存分配行为与 printf 基本一致,但通过 `setvbuf` 使用静态缓冲区可完全规避其动态分配[^4]。在安全关键系统中,应彻底禁用动态格式说明符。 --- ### 相关问题 1. 如何检测标准库函数中隐藏的内存分配调用? 2. `setvbuf` 函数的工作原理是什么?如何用它优化文件 I/O 性能? 3. 除了 printf/fprintf,还有哪些看似"安全"的标准库函数会隐式调用 malloc? 4. 在实时操作系统中如何实现完全无动态内存分配的日志系统? 5. Glibc 的 `_FORTIFY_SOURCE` 机制如何防止格式化字符串漏洞?[^4] [^1]: 动态内存分配原理 [^4]: Glibc 内存安全机制
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值