【C++入门到精通】C++入门 —— 内存管理(new函数的讲解)上

简介: 【C++入门到精通】C++入门 —— 内存管理(new函数的讲解)上

前言


       前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象。也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C和C++比较重要的知识点——内存管理。下面话不多说坐稳扶好咱们要开车了😍


一、C/C++内存分布


        C/C++的内存分布较为复杂,大概分为五个部分它们分别是:栈(Stack)、堆(Heap)、全局区/静态区(Global Area/Static Area)、常量区(Constant Area)、代码区(Code Area)下面我会分别介绍他们各自的属性。


1. 栈(Stack)


🍟栈是用来存储函数的局部变量、函数参数和函数调用的返回地址等信息的内存空间。

🍟栈空间由编译器自动管理,它的分配和释放是自动进行的,不需要程序员手动操作。

🍟每当一个函数被调用时,编译器会在栈上为该函数分配一块特定大小的空间,称为函数帧或活动记录(Activation Record)。

🍟栈是一种高效的存储方式,由于栈上的内存是连续分配的,因此访问速度较快。然而,栈的大小是有限的,过多的栈帧可能导致栈溢出的错误。


2. 堆(Heap)


🍁堆是用来动态分配内存的空间,通过使用malloc、free、new、delete等操作来分配和释放内存。

🍁堆的分配是手动进行的,程序员需要显式地分配和释放内存。

🍁堆是一种灵活的存储方式,允许动态调整分配的内存大小。

🍁堆上的内存分配通常是不连续的,因此访问速度稍慢于栈。

🍁如果使用不当,堆上的内存可能导致内存泄漏或内存碎片化等问题。


3. 全局区/静态区(Global Area/Static Area)


🍔全局区用来存储全局变量和静态变量。

🍔全局变量在程序开始执行时分配,在程序结束时被释放,因此它们的生命周期与整个程序的运行周期相同。

🍔静态变量有不同的存储位置和生命周期,具体取决于其作用域和存储类型(例如静态局部变量、静态全局变量)。

🍔全局区的内存空间在程序启动时由操作系统分配,并在程序退出时由操作系统释放。


4. 常量区(Constant Area)


🍪常量区用来存储常量数据,例如字符串字面量和其他常量。

🍪常量区的内存通常是只读的,因此程序不能对其进行修改。

🍪常量区的数据在程序运行期间保持不变。


5. 代码区(Code Area)


🥝代码区用来存储程序的机器指令,也称为可执行代码。

🥝代码区通常是只读的,存储程序的执行逻辑。

🥝代码区的内存空间在程序启动时被分配,并且在程序运行期间保持不变。


e900c8a9001b0b7d924543a38cd4e384_68b9dc8a8a814f08bdafd4190a9ea669.png


        🚨注意:具体的内存布局和分配方式可能因编译器、操作系统和硬件平台的不同而有所差异。此外,在多线程和多进程的情况下,还需要考虑线程栈、共享内存等特殊情况。


二、C语言中动态内存管理方式


1. malloc函数


  • malloc 函数用于动态地分配指定大小的内存空间。
  • 它接受一个参数,即需要分配的内存大小(以字节为单位),并返回所分配内存的起始地址。
int ptr = (int)malloc(sizeof(int));
  • malloc 函数在堆上找到足够大的连续内存空间进行分配,如果找不到足够大的连续空间,则返回 NULL
  • 分配的内存是未初始化的,即其中的数据是不确定的,需要手动初始化。
#include <stdio.h>
#include <stdlib.h>
int main() {
    int size;
    int* arr;
    printf("Enter the size of the array: ");
    scanf("%d", &size);
    // 使用malloc函数动态分配内存
    arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }
    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }
    printf("The elements you entered are: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    // 释放已分配的内存
    free(arr);
    return 0;
}


上面的程序实现了以下步骤:


  1. 用户输入数组的大小。
  2. 使用 malloc 函数动态分配足够大的内存空间来存储整数数组。
  3. 检查内存分配是否成功,如果分配失败,则输出错误信息并结束程序。
  4. 用户输入数组的元素。
  5. 打印用户输入的数组元素。
  6. 使用 free 函数释放分配的内存空间。


2. calloc函数


  • calloc 函数用于动态地分配指定数量和大小的内存空间,并将其初始化为0。
  • 它接受两个参数,第一个是需要分配的元素数量,第二个是每个元素的大小(以字节为单位)。
int ptr = (int)calloc(5, sizeof(int));
  • calloc 函数在堆上找到足够大的内存空间进行分配,并将所有字节初始化为0。
  • 分配的内存是初始化过的,不需要手动初始化。
#include <stdio.h>
#include <stdlib.h>
int main() {
    int size;
    int* arr;
    printf("Enter the size of the array: ");
    scanf("%d", &size);
    // 使用calloc函数动态分配内存,并将内存初始化为0
    arr = (int*)calloc(size, sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }
    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }
    printf("The elements you entered are: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    // 释放已分配的内存
    free(arr);
    return 0;
}


       上面的代码功能跟上面 malloc 函数的示例表达的功能一样这里不做过多的解释,记得在完成使用动态分配的内存后,使用 free 函数释放已分配的内存,以免出现内存泄漏。


3. realloc函数


  • realloc 函数用于重新调整之前分配的内存空间的大小。
  • 它接受两个参数,第一个是之前分配内存的起始地址,第二个是需要调整的新大小(以字节为单位)
ptr = (int*)realloc(ptr, 10 * sizeof(int));
  • realloc 函数将尝试在原始内存空间上重新调整大小,如果成功,则返回调整后的内存地址;如果原始内存空间不够大或者其他错误,则返回 NULL
  • 若新大小大于原始大小,则新增的字节未初始化;若新大小小于原始大小,则超出新大小的部分将被丢弃。
#include <stdio.h>
#include <stdlib.h>
int main() {
    int size;
    int* arr;
    printf("Enter the size of the array: ");
    scanf("%d", &size);
    // 使用malloc函数动态分配内存
    arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }
    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }
    // 重新分配内存,将数组大小调整为10
    int newSize = 10;
    int* newArr = (int*)realloc(arr, newSize * sizeof(int));
    if (newArr == NULL) {
        printf("Memory reallocation failed!");
        free(arr);  // 释放之前分配的内存
        return 1;
    }
    // 更新指针arr的引用
    arr = newArr;
    // 打印数组元素
    printf("The elements you entered are: ");
    for (int i = 0; i < newSize; i++) {
        printf("%d ", arr[i]);
    }
    // 释放分配的内存
    free(arr);
    return 0;
}


        首先使用 malloc 函数动态分配内存,并在之后的 realloc 操作中进行了适当的错误处理。如果 realloc 函数返回 NULL ,表示内存调整失败,此时会输出错误信息并释放之前分配的内存。如果 realloc 函数成功,则将新分配的内存地址赋给 arr 指针,并继续使用调整后的内存。在最后,使用 free 函数释放分配的内存空间,并返回 0 表示程序正常结束


4. free函数


  • free 函数用于释放之前动态分配的内存空间。
  • 传入 free 函数的参数是之前分配的内存块的起始地址。
  • 调用free函数将释放所指定的内存空间,使其可供之后的malloc、calloc、realloc等函数重新使用。

       这些动态内存管理函数提供了灵活的内存分配和释放能力,可以根据需要动态调整内存的大小。然而,需要确保正确使用,避免内存泄漏和野指针等问题。建议在每次分配内存后检查分配是否成功,以及在适当的时候使用 free 函数释放不再需要的内存。


void Test ()
{
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);
    // 这里需要free(p2)吗?  答:不用
    free(p3 );
}

       上面的代码中不需要 free(p2) 因为当调用realloc函数时,如果成功调整了内存大小,那么原始内存区域的内容将被复制到新分配的内存,并且原始的内存空间会被释放。因此,在这种情况下,不需要再手动调用 free(p2) 来释放原始的内存空间,因为realloc函数已经负责处理了。


目录
相关文章
|
4月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
306 15
|
5月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
150 0
|
8月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
454 6
|
11月前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
398 6
|
4月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
1475 0
|
4月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
430 0
|
4月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
413 0
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
897 0