【C/C++】——小白初步了解——内存管理

news2024/11/29 14:56:02

目录

1. C/C++内存分布

代码区(Code Segment):

数据区(Data Segment):

堆区(Heap):

栈区(Stack):

常量区(Constant Segment):

2. C语言中动态内存管理方式

1.malloc(size_t size):

2.calloc(size_t nmemb, size_t size):

3.*realloc(void ptr, size_t size):

4.*free(void ptr):

3. C++中动态内存管理

1.new:

2.delete:

4. operator new与operator delete函数

5. new和delete的实现原理

6. 定位new表达式(placement-new)

7. 常见面试题

1.解释C++中new和malloc的区别

2.什么是内存泄漏?如何避免?

3.解释C++中的RAII(Resource Acquisition Is Initialization)

4.解释栈区和堆区的区别

5.如何实现自己的内存池?


1. C/C++内存分布

一个典型的C/C++程序在内存中的布局如下:

  1. 代码区(Code Segment):

    • 存储程序的机器指令,由编译器生成。
    • 该区域通常是只读的,以防止程序在运行时修改自身的指令。
    • 代码区在程序加载时被操作系统加载到内存中。
  2. 数据区(Data Segment):

    • 存储全局变量和静态变量,包括已初始化和未初始化的变量。
    • 数据区又分为两部分:
      • 已初始化数据区(Initialized Data Segment): 存储程序中已初始化的全局变量和静态变量。
      • 未初始化数据区(Uninitialized Data Segment or BSS): 存储未初始化的全局变量和静态变量,程序启动时这些变量会被初始化为0。
  3. 堆区(Heap):

    • 用于动态内存分配,大小不固定,可以在程序运行时动态地增长或缩小。
    • 由程序员手动管理内存的分配和释放。常用的函数有 malloc()free()
    • 堆区的内存分配效率较低,但灵活性高。
  4. 栈区(Stack):

    • 用于函数调用时的临时存储,包括函数的局部变量、参数和返回地址。
    • 栈区的内存由编译器自动分配和释放,具有后进先出的特点。
    • 栈区内存分配效率高,但大小有限,通常由操作系统决定。
  5. 常量区(Constant Segment):

    • 存储常量数据,如字符串字面量和常量变量。
    • 通常也是只读的,以保护常量数据不被修改。

2. C语言中动态内存管理方式

在C语言中,动态内存管理主要通过以下几个函数实现:

1.malloc(size_t size):

  • 功能:分配指定大小的字节,并返回一个指向这块内存的指针。

  • 特点:分配的内存未初始化,内容是随机的。

  • #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int *arr = (int *)malloc(10 * sizeof(int));
        if (arr == NULL) {
            printf("Memory allocation failed\n");
            return 1;
        }
        for (int i = 0; i < 10; i++) {
            arr[i] = i;
        }
        for (int i = 0; i < 10; i++) {
            printf("%d ", arr[i]);
        }
        free(arr);
        return 0;
    }
    

    2.calloc(size_t nmemb, size_t size):

  • 功能:分配nmemb个元素,每个元素size字节,并初始化所有分配的字节为0。

  • 特点:分配的内存被初始化为0,适合分配需要清零的数组。

  • #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int *arr = (int *)calloc(10, sizeof(int));
        if (arr == NULL) {
            printf("Memory allocation failed\n");
            return 1;
        }
        for (int i = 0; i < 10; i++) {
            printf("%d ", arr[i]);
        }
        free(arr);
        return 0;
    }
    

    3.*realloc(void ptr, size_t size):

  • 功能:调整之前分配的内存块的大小。

  • 特点:如果新大小大于原大小,新分配的内存区域中的内容是不确定的;如果新大小小于原大小,超出的内容将被丢弃。

  • #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int *arr = (int *)malloc(5 * sizeof(int));
        if (arr == NULL) {
            printf("Memory allocation failed\n");
            return 1;
        }
        for (int i = 0; i < 5; i++) {
            arr[i] = i;
        }
        arr = (int *)realloc(arr, 10 * sizeof(int));
        if (arr == NULL) {
            printf("Memory allocation failed\n");
            return 1;
        }
        for (int i = 5; i < 10; i++) {
            arr[i] = i;
        }
        for (int i = 0; i < 10; i++) {
            printf("%d ", arr[i]);
        }
        free(arr);
        return 0;
    }
    

    4.*free(void ptr):

  • 功能:释放之前分配的内存块,使其可以重新分配。

  • 特点:释放后,指针ptr不再指向有效的内存区域,应该将ptr置为NULL以防止野指针错误。

  • #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int *arr = (int *)malloc(10 * sizeof(int));
        if (arr == NULL) {
            printf("Memory allocation failed\n");
            return 1;
        }
        free(arr);
        arr = NULL;
        return 0;
    }
    

    3. C++中动态内存管理

    在C++中,动态内存管理不仅可以使用C语言的函数(如malloc、calloc等),还提供了更高级的 newdelete 运算符:

  • 1.new:

    • 功能:分配指定类型的内存,并调用该类型的构造函数。

    • 示例代码:

      #include <iostream>
      
      int main() {
          int *arr = new int[10];
          for (int i = 0; i < 10; i++) {
              arr[i] = i;
          }
          for (int i = 0; i < 10; i++) {
              std::cout << arr[i] << " ";
          }
          delete[] arr;
          return 0;
      }
      

      2.delete:

  • 功能:释放用new分配的内存,并调用该类型的析构函数。
#include <iostream>

int main() {
    int *arr = new int[10];
    delete[] arr;
    return 0;
}

在C++中,使用 newdelete 操作符进行内存管理比使用C语言中的函数更方便,因为它们不仅分配和释放内存,还自动调用构造函数和析构函数,确保对象在创建和销毁时执行必要的初始化和清理工作。

4. operator new与operator delete函数

C++中,operator newoperator delete 是为对象分配和释放内存的函数。它们类似于 mallocfree,但有一些重要区别:

  • operator new:

    • 功能:分配指定大小的内存,但不调用构造函数。

    • 通常在类的new运算符中隐式调用。

    • #include <iostream>
      
      void* operator new(size_t size) {
          std::cout << "Custom new for size " << size << std::endl;
          return malloc(size);
      }
      
      void operator delete(void* ptr) noexcept {
          std::cout << "Custom delete" << std::endl;
          free(ptr);
      }
      
      int main() {
          int *p = new int(10);
          delete p;
          return 0;
      }
      

    • operator delete:

      • 功能:释放用 operator new 分配的内存,但不调用析构函数。
      • 通常在类的delete运算符中隐式调用。
    • 可以重载这两个函数以定制内存分配行为。例如,在需要跟踪内存分配和释放的场景中,可以重载 operator newoperator delete 以记录每次内存操作的日志。

      5. new和delete的实现原理

      newdelete 的实现可以分为两个步骤:

    • new:

      • 调用 operator new 分配内存。
      • 在分配的内存上调用构造函数初始化对象。
    • delete:

      • 在内存上调用析构函数销毁对象。
      • 调用 operator delete 释放内存。
    • 示例代码展示了new和delete的工作机制:

      #include <iostream>
      
      class MyClass {
      public:
          MyClass() {
              std::cout << "Constructor called" << std::endl;
          }
          ~MyClass() {
              std::cout << "Destructor called" << std::endl;
          }
      };
      
      int main() {
          MyClass *obj = new MyClass();
          delete obj;
          return 0;
      }
      

在上面的代码中,当我们使用 new MyClass() 创建对象时,首先调用 operator new 分配内存,然后在分配的内存上调用 MyClass 的构造函数。当我们使用 delete obj 删除对象时,首先调用 MyClass 的析构函数,然后调用 operator delete 释放内存。

6. 定位new表达式(placement-new)

placement new 是C++中的一个高级特性,用于在已分配的内存上构造对象。它不会分配新的内存,只是调用对象的构造函数。

#include <iostream>

int main() {
    char buffer[sizeof(int)];
    int *p = new (buffer) int(5); // 在buffer中构造int
    std::cout << *p << std::endl;
    p->~int(); // 手动调用析构函数
    return 0;
}

在上面的代码中,我们在预先分配的内存 buffer 中使用 placement new 构造了一个 int 对象。这种技术通常用于自定义内存池或优化程序性能。

7. 常见面试题

1.解释C++中new和malloc的区别

  • new:
    • 分配内存并调用构造函数初始化对象。
    • 返回对象的指针。
    • 可以重载。
    • 用于分配类对象。
  • malloc:
    • 仅分配内存,不调用构造函数。
    • 返回 void* 类型的指针,需要类型转换。
    • 不能重载。
    • 用于分配任意类型的内存。

2.什么是内存泄漏?如何避免?

  • 内存泄漏: 是指程序在分配内存后,未能正确释放已分配的内存,导致内存无法被重新利用。
  • 避免方法:
    • 使用智能指针(如 std::unique_ptrstd::shared_ptr)来自动管理内存。
    • 确保每个 malloc 对应一个 free,每个 new 对应一个 delete
    • 使用工具如 Valgrind 进行内存泄漏检测。

3.解释C++中的RAII(Resource Acquisition Is Initialization)

  • RAII: 是一种编程习惯,即资源的获取和释放通过对象的构造函数和析构函数来管理。
  • 示例:
    #include <iostream>
    #include <memory>
    
    class Resource {
    public:
        Resource() { std::cout << "Resource acquired" << std::endl; }
        ~Resource() { std::cout << "Resource released" << std::endl; }
    };
    
    void useResource() {
        std::unique_ptr<Resource> res(new Resource());
        // 使用资源
    }
    
    int main() {
        useResource();
        return 0;
    }
    

    4.解释栈区和堆区的区别

    • 栈区:
      • 用于存储函数调用的局部变量。
      • 内存由编译器自动分配和释放。
      • 具有后进先出的特点。
      • 内存分配效率高,但大小有限。
    • 堆区:
      • 用于动态内存分配。
      • 内存由程序员手动分配和释放。
      • 大小不固定,可以动态增长或缩小。
      • 内存分配效率较低,但灵活性高。

5.如何实现自己的内存池?

内存池是一种预分配大块内存以减少多次分配开销的方法。可以通过链表管理内存块,分配时从链表中取出一块内存,释放时将内存块重新挂回链表。

#include <iostream>
#include <vector>

class MemoryPool {
    std::vector<void*> pool;
public:
    MemoryPool(size_t size, size_t count) {
        for (size_t i = 0; i < count; ++i) {
            pool.push_back(malloc(size));
        }
    }

    void* allocate() {
        if (pool.empty()) return malloc(1); // 返回新分配内存
        void* ptr = pool.back();
        pool.pop_back();
        return ptr;
    }

    void deallocate(void* ptr) {
        pool.push_back(ptr);
    }

    ~MemoryPool() {
        for (auto ptr : pool) {
            free(ptr);
        }
    }
};

int main() {
    MemoryPool pool(256, 10);
    void* p1 = pool.allocate();
    pool.deallocate(p1);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1802113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring boot 2.1 集成activiti 6.0.0和activiti-modeler 5.23.0可视化编辑器(随记)

先上pom&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.or…

AI高考大战,揭秘五大热门模型谁能问鼎数学之巅?

在高考前&#xff0c;我就有想法了&#xff0c;这一次让AI来做做高考题。就用国内的大模型&#xff0c;看哪家的大模型解题最厉害。 第一天考完&#xff0c;就拿到了2024高考数学2卷的电子版&#xff0c;这也是重庆市采用的高考试卷 这次选了5个AI工具&#xff0c;分别是天工&a…

【机器学习】与【数据挖掘】技术下【C++】驱动的【嵌入式】智能系统优化

目录 一、嵌入式系统简介 二、C在嵌入式系统中的优势 三、机器学习在嵌入式系统中的挑战 四、C实现机器学习模型的基本步骤 五、实例分析&#xff1a;使用C在嵌入式系统中实现手写数字识别 1. 数据准备 2. 模型训练与压缩 3. 模型部署 六、优化与分析 1. 模型优化 模…

hot100 -- 二分查找

目录 前言 &#x1f382;搜索插入位置 &#x1f33c;搜索二维矩阵 &#x1f33c;排序数组元素第一和最后一个位置 &#x1f33c;旋转排序数组 &#x1f4aa;旋转排序数组中的最小值 &#x1f4aa;两个正序数组的中位数 前言 二分算法学习_时间超限ac:0%-CSDN博客 &#…

Vue10-事件修饰符

一、示例&#xff1a;<a>标签不执行默认的跳转行为 1-1、方式一 <a href"http://www.baidu.com" onclick"event.preventDefault();">点击我</a> 1-2、方式二 1-3、方式三&#xff1a;事件修饰符 二、Vue的六种事件修饰符 2-1、prevent&…

Edge怎么关闭快捷键

Edge怎么关闭快捷键 在Edge浏览器中&#xff0c;你可以通过以下步骤关闭快捷键&#xff1a; 打开Edge浏览器&#xff0c;输入&#xff1a;edge://flags 并按下回车键。 在Flags页面中&#xff0c;搜索“快捷键”(Keyboard shortcuts)选项。 将“快捷键”选项的状态设置为“…

注册小程序

每个小程序都需要在 app.js 中调用 App 方法注册小程序实例&#xff0c;绑定生命周期回调函数、错误监听和页面不存在监听函数等。 详细的参数含义和使用请参考 App 参考文档 。 整个小程序只有一个 App 实例&#xff0c;是全部页面共享的。开发者可以通过 getApp 方法获取到全…

HuggingFace团队亲授大模型量化基础: Quantization Fundamentals with Hugging Face

Quantization Fundamentals with Hugging Face 本文是学习https://www.deeplearning.ai/short-courses/quantization-fundamentals-with-hugging-face/ 这门课的学习笔记。 What you’ll learn in this course Generative AI models, like large language models, often exce…

转让无区域商业管理公司挺批行业包变更

无区域的名称我们可以直接进行名称的申请核准。 从新规施行之后&#xff0c;国家局核名批准难度更高。新申请的无区域名称已经停批了&#xff0c;进行核名将更难&#xff0c;而需要满足一定条件并在成立一年后才能变更升级名称。而这个过程并非易事&#xff0c;难度非常高。可以…

45-1 waf绕过 - 文件上传绕过WAF方法

环境准备: 43-5 waf绕过 - 安全狗简介及安装-CSDN博客然后安装dvwa靶场:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客打开dvwa靶场,先将靶场的安全等级调低,然后切换到文件上传 一、符号变异 在PHP中,由于其弱类型特性,有时候仅有一…

JavaSE——抽象类和接口

目录 一 .抽象类 1.抽象类概念 2.抽象类语法 3.抽象类特性 4.抽象类的作用 二. 接口 1.接口的概念 2.语法规则 3.接口的使用 4.接口特性 5.实现多个接口 6.接口间的继承 三.抽象类和接口的区别 一 .抽象类 1.抽象类概念 在面向对象的概念中&#xff0c;所有的对…

SpringCloud整合OpenFeign实现微服务间的通信

1. 前言 1.1 为什么要使用OpenFeign&#xff1f; 虽说RestTemplate 对HTTP封装后, 已经⽐直接使⽤HTTPClient简单⽅便很多, 但是还存在⼀些问题. 需要拼接URL, 灵活性⾼, 但是封装臃肿, URL复杂时, 容易出错. 代码可读性差, ⻛格不统⼀。 1.2 介绍一下微服务之间的通信方式 微…

Zabbix实现邮件和钉钉实时告警(使用python脚本)

告警和通知 告警是监控的主要职能,是指将到达某一阈值事件的消息发送给用户,让用户在事件发生的时候即刻知道监控项处于不正常状态,从而让用户来决定是否采取相关措施。 zabbix中,告警是由一系列的流程组成的,⾸首先是触发器到达阈值,接下是Active对事件信息进行处理,其…

TCP/IP协议分析实验:通过一次下载任务抓包分析

TCP/IP协议分析 一、实验简介 本实验主要讲解TCP/IP协议的应用&#xff0c;通过一次下载任务&#xff0c;抓取TCP/IP数据报文&#xff0c;对TCP连接和断开的过程进行分析&#xff0c;查看TCP“三次握手”和“四次挥手”的数据报文&#xff0c;并对其进行简单的分析。 二、实…

ElasticSearch学习笔记之一:介绍及EFK部署

1. 系统概述 The Elastic Stack&#xff0c;包括Elasticsearch、Kibana、Beats和Logstash&#xff08;也成为ELK Stack&#xff09; Elasticsearch&#xff1a;简称ES&#xff0c;是一个开源的高扩展的分布式全文搜索引擎&#xff0c;是整个Elastic Stack技术栈的核心。它可以…

【MySQL】聊聊MySQL常见的SQL语句阻塞场景

在平时的业务中&#xff0c;可能一个简单的SQL语句也执行很慢&#xff0c;这种情况其实大多数都是要么没有使用索引&#xff0c;要么出现锁竞争造成执行阻塞。本篇主要来介绍具体的场景 CREATE TABLE t ( id int(11) NOT NULL, c int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENG…

用HAL库改写江科大的stm32入门-6-4 PWM驱动舵机

接线图&#xff1a; 如何控制一个舵机 舵机的控制由一个脉冲宽度调制信号(PWM波&#xff09;来实现&#xff0c;该信号在这个实验里使用stm32来发出。 舵机通讯协议&#xff1a; 对应设置参数&#xff1a; ARR的值为19999 CCR的值为500~2500(生成占空比是2.5%~12.5%的波形)…

[经验] 昆山教育网(昆山教育网中小学报名) #媒体#职场发展#微信

昆山教育网&#xff08;昆山教育网中小学报名&#xff09; 昆山教育局网站 网站&#xff1a;昆山市教育局 昆山市教育局全面贯彻执行党和国家的教育方针、政策&#xff0c;落实有关教育工作的法律、法规&#xff1b;负责制定本市教育工作的实施意见和措施&#xff0c;并监督…

Java基础_异常

Java基础_异常 异常体系介绍编译时异常和运行时异常异常的作用异常的处理方式JVM默认的处理方式自己处理&#xff08;捕获异常&#xff09;try...catch灵魂四问Throwable的成员方法 抛出处理 综合练习自定义异常来源Gitee地址 异常体系介绍 异常是什么&#xff1f; 程序中可能出…

LabVIEW2017破解安装教程

LabVIEW2017破解安装教程&#xff1a; 1、新版LabVIEW2017分为32位和64位两个平台&#xff0c;多种语言版本(需要LabVIEW2017中文版的朋友请选择WinChn版本)&#xff0c;大家选择自行选择符合系统的版本下载并解压 2、本次安装以Win 7 64位系统为例&#xff0c;运行“2017LV-64…