C++从入门到实战(十一)详细讲解C/C++语言中内存分布与C与C++内存管理对比

article/2025/6/26 6:16:39

C++从入门到实战(十一)详细讲解C/C++语言中内存分布与C与C++内存管理对比

  • 前言
  • 一、C/C++语言中内存分布
    • 1.内核空间
    • 2.栈
    • 3.堆
    • 4.数据段
    • 5.代码段
  • 二、例题带练巩固C/C++语言中内存分布的知识
    • 题目讲解
    • 题目答案
  • 三、C语言动态内存分配(知识回顾)
    • 3.1 为什么需要动态内存分配
    • 3.2 malloc 函数
    • 3.3 calloc 函数
    • 3.4 realloc 函数
    • 3.5 free 函数
    • 3.6 C语言动态内存分配的缺点
  • 四、C++动态内存分配
    • 1. new 和 delete
    • 2. new[] 和 delete[] 运算符
  • 五、C与C++内存管理对比
    • 5.1 C语言内存管理
    • 5.2 C++内存管理
    • 5.3 C 与 C++ 内存管理对比表格


前言

  • 在之前的博客系列中,我们深入探讨了C++的第一个重要阶段——类和对象,以及与之相关的诸多核心内容,包括四大构造函数、类的种类、内部类、匿名类、友元等
  • 这些知识点如同坚实的基石,为后续的C++学习构建了稳固的基础,帮助我们逐步建立起对C++面向对象编程的深刻理解。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

  • 接下来,我们将开启新的篇章,深入剖析C/C++语言中的内存分布,并对比C与C++在内存管理方面的差异。

一、C/C++语言中内存分布

在这里插入图片描述

1.内核空间

  • 内核空间(Kernel Space):相当于 “管理层办公室”,存放着操作系统的核心代码(比如管理 CPU、内存、硬盘的程序)。只有 “管理层”(内核程序)能进去,普通程序不能随便访问,权限很高,负责处理最底层的硬件资源。

2.栈

  • 栈是一种后进先出(LIFO)的数据结构,用于存储局部变量、函数调用信息等
  • 每当调用一个函数时,系统会在栈上为该函数的局部变量分配内存,函数执行完毕后,这些内存会被自动释放。

想象你去食堂打饭,餐盘是按顺序叠起来的:后放上去的餐盘先被拿走(后进先出)。

  • 栈就是这样一个 “自动清理的临时货架”,存放函数运行时的 临时数据(比如局部变量、函数参数、返回地址)。程序会自动分配和释放空间,不需要你手动管理
// 函数,函数代码存储在代码段
void func() {// 局部变量,存储在栈上int local_variable = 30;std::cout << "Local variable: " << local_variable << std::endl;
}// 调用函数,函数调用信息和局部变量会在栈上操作int main(){func();}

3.堆

堆是用于动态分配内存的区域,程序可以在运行时从堆中请求和释放内存

想象你去超市存包,储物柜可以按需申请:你觉得需要多大空间,就租多大的柜子,用完自己关门(释放),不关门的话柜子会一直被占着(内存泄漏)

  • 堆就是这样一个 “动态分配的内存区域”,程序运行时可以主动申请(如 C 语言的 malloc、Python 的 list 扩容)和释放(free),空间大小不固定,灵活但需要自己管理
 // 使用 malloc 分配内存int *ptr_malloc = (int *)malloc(5 * sizeof(int));// 使用 calloc 分配内存int *ptr_calloc = (int *)calloc(5, sizeof(int));// 使用 realloc 调整内存大小ptr_malloc = (int *)realloc(ptr_malloc, 10 * sizeof(int));// 释放内存free(ptr_malloc);free(ptr_calloc);
  • 上面的代码均在堆上执行

4.数据段

  • 数据段用于存储全局变量和静态变量
  • 全局变量在程序的整个生命周期内都存在,并且可以被程序的任何部分访问。
  • 数据段的代码程序运行时一直存在,直到程序结束才释放
// 全局变量,存储在数据段
int global_variable = 10;// 静态变量,存储在数据段
static int static_variable = 20;void func() {static int static_age = 18;  // 静态局部变量,也存在数据段
}

5.代码段

  • 代码段存放程序的可执行代码(二进制指令),比如我们写的 if、for、函数逻辑等,运行时只能被执行,不能被修改(防止程序运行时代码被破坏)

在这里插入图片描述

二、例题带练巩固C/C++语言中内存分布的知识

  • 学习完上面的知识,我们用一道典型的企业面试常考例题来巩固一下我们刚刚学到的知识

我们来看下面的一段代码和相关问题

int globalVar = 1;static int staticGlobalVar = 1;void Test(){static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);}
  • 题目

在这里插入图片描述

题目讲解

int globalVar = 1;static int staticGlobalVar = 1;void Test(){static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);}
  • 结合我们刚刚讲的知识和例子和知识

  • globalVar:全局变量,存放在 数据段(静态区)数据段用于存放全局变量和静态变量。

  • staticGlobalVar:静态全局变量,也在 数据段(静态区)。静态全局变量本质还是全局变量,只是作用域受限。

  • staticVar:静态局部变量,存于 数据段(静态区)。静态局部变量生命周期长,存储在数据段

  • localVar:局部变量,存于 栈 中。栈用于存放局部变量,由系统自动分配释放。

  • num1:局部数组,也存于 栈 中,数组作为局部变量,空间在栈中分配。

  • char2:字符数组(局部),存于 栈。数组本身是局部变量,元素空间在栈中。

  • *char2:char2 是栈中的数组,*char2 访问栈中数组元素,存于 栈。

  • pChar3:指针变量(局部),存于 栈。指针本身作为局部变量,空间在栈。

  • *pChar3:pChar3 指向字符串常量 “abcd”,字符串常量存于 代码段(常量区)。代码段存放常量

  • ptr1:指针变量(局部),存于 栈。指针本身是局部变量,在栈中。

  • *ptr1:ptr1 指向 malloc 分配的内存,malloc 分配的内存来自 堆。堆用于动态内存分配。

题目答案

  • 结合我们刚刚的知识。想必大家已经搞定了刚刚的例题,下面我们将例题的答案放出来
    在这里插入图片描述

三、C语言动态内存分配(知识回顾)

  • 在正式开始C++的new与delete之前,我们需要回顾并补充一些知识C语言的知识,以便我们后续的理解

3.1 为什么需要动态内存分配

在许多场景下,我们在编写代码时可能并不清楚程序运行时到底需要多少内存。

  • 例如,你要存储用户输入的一串字符,可用户输入的字符数量并不固定。
  • 这种情况下,动态内存分配就能派上用场,它可以让程序在运行时根据实际需求来分配内存。

C语言提供了几个用于动态内存分配的函数,这些函数都在 <stdlib.h> 头文件中

3.2 malloc 函数

malloc 函数用于分配指定大小的内存块,其原型如下:

void* malloc(size_t size);

size 表示要分配的内存字节数,若分配成功,函数会返回一个指向该内存块起始位置的指针;若分配失败,则返回 NULL

下面是一个简单的示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 分配一个能存储 5 个整数的内存块int *ptr = (int *)malloc(5 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}// 给分配的内存块赋值for (int i = 0; i < 5; i++) {ptr[i] = i;}// 打印内存块中的值for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");// 释放分配的内存free(ptr);return 0;
}

在这里插入图片描述

3.3 calloc 函数

calloc 函数和 malloc 类似,不过它会把分配的内存初始化为 0。其原型如下:

void* calloc(size_t num, size_t size);

num 代表要分配的元素数量,size 表示每个元素的大小。

以下是使用 calloc 的示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 分配一个能存储 5 个整数的内存块,并初始化为 0int *ptr = (int *)calloc(5, sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}// 打印内存块中的值for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");// 释放分配的内存free(ptr);return 0;
}

在这里插入图片描述

3.4 realloc 函数

realloc 函数用于调整已分配内存块的大小。其原型如下:

void* realloc(void* ptr, size_t size);

ptr 是指向已分配内存块的指针,size 是新的内存块大小。若分配成功,函数会返回一个指向新内存块起始位置的指针;若失败,则返回 NULL

下面是使用 realloc 的示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 分配一个能存储 5 个整数的内存块int *ptr = (int *)malloc(5 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}// 给分配的内存块赋值for (int i = 0; i < 5; i++) {ptr[i] = i;}// 调整内存块大小,使其能存储 10 个整数ptr = (int *)realloc(ptr, 10 * sizeof(int));if (ptr == NULL) {printf("内存重新分配失败\n");return 1;}// 给新分配的内存块赋值for (int i = 5; i < 10; i++) {ptr[i] = i;}// 打印内存块中的值for (int i = 0; i < 10; i++) {printf("%d ", ptr[i]);}printf("\n");// 释放分配的内存free(ptr);return 0;
}

在这里插入图片描述

3.5 free 函数

free 函数用于释放之前动态分配的内存。其原型如下:

void free(void* ptr);

ptr 是指向要释放的内存块的指针。一旦内存被释放,就不能再使用该指针访问这块内存了。

3.6 C语言动态内存分配的缺点

  • 在我们刚刚的代码中,我们发现
  • 在 C 语言中,使用malloc、calloc和realloc函数进行动态内存分配时,返回的是void*类型的指针
  • 这就要求程序员手动将其转换为所需的指针类型,若转换出错,就可能在运行时引发难以调试的错误
  • 而且,C 语言主要是面向过程的语言,动态分配的内存只是简单的字节块,无法自动调用对象的构造函数和析构函数。
  • 在处理复杂的数据类型(如类对象)时,就需要手动管理对象的初始化和清理工作,容易出错
  • C 语言没有内置的异常处理机制,在内存分配失败时,通常只能通过返回NULL指针来表示错误,程序员需要手动检查返回值并进行相应处理,代码会变得繁琐

因此C++ 引入了new和delete运算符来进行动态内存分配和释放,它们能很好地解决 C 语言动态内存分配的部分问题

四、C++动态内存分配

1. new 和 delete

C++ 引入了new和delete运算符来进行动态内存分配和释放,它们能很好地解决 C 语言动态内存分配的部分问题

  • new 运算符:用于动态分配内存,同时会自动调用对象的构造函数。
  • delete 运算符:用于释放动态分配的内存,同时会自动调用对象的析构函数
#include <iostream>class MyClass {
public:MyClass() {std::cout << "构造函数被调用" << std::endl;}~MyClass() {std::cout << "析构函数被调用" << std::endl;}
};int main() {// 使用new分配内存并创建对象MyClass *obj = new MyClass();// 使用对象// ...// 使用delete释放内存delete obj;return 0;
}

在这里插入图片描述

2. new[] 和 delete[] 运算符

  • 若要动态分配数组,可使用new[]和delete[]运算符

#include <iostream>class MyClass {
public:MyClass() {std::cout << "构造函数被调用" << std::endl;}~MyClass() {std::cout << "析构函数被调用" << std::endl;}void printMessage() {std::cout << "这是 MyClass 类对象的消息。" << std::endl;}
};int main() {// 使用new[]分配数组内存MyClass* arr = new MyClass[3];// 使用数组,遍历数组并调用成员函数for (int i = 0; i < 3; ++i) {arr[i].printMessage();}// 使用delete[]释放数组内存delete[] arr;return 0;
}eturn 0;
}

在这里插入图片描述

五、C与C++内存管理对比

5.1 C语言内存管理

C语言主要通过标准库函数来进行内存管理,核心函数有 malloccallocreallocfree。下面是这些函数的详细介绍:

  • malloc:用来分配指定大小的内存块,返回的是 void* 类型指针,需要手动进行类型转换。分配的内存内容是未初始化的。
  • calloc:功能和 malloc 类似,不过它会把分配的内存初始化为 0。
  • realloc:用于调整已经分配的内存块大小,可以扩大或缩小。
  • free:释放之前动态分配的内存,释放后该内存可被系统重新使用。

5.2 C++内存管理

C++ 除了可以使用 C 语言的内存管理函数,还引入了 newdelete 运算符来进行动态内存管理。

  • new:用于动态分配内存,会自动调用对象的构造函数。对于单个对象使用 new,对于数组使用 new[]
  • delete:用于释放 new 分配的内存,会自动调用对象的析构函数。对应 new 使用 delete,对应 new[] 使用 delete[]

5.3 C 与 C++ 内存管理对比表格

对比项C 语言C++
内存分配函数/运算符malloccallocreallocnewnew[]
内存释放函数/运算符freedeletedelete[]
类型安全性返回 void* 指针,需要手动类型转换,类型安全性低直接返回正确类型的指针,无需手动转换,类型安全性高
对象构造和析构不支持自动调用构造和析构函数,需要手动管理自动调用构造和析构函数,简化对象生命周期管理
异常处理内存分配失败返回 NULL,需手动检查内存分配失败抛出 std::bad_alloc 异常,可使用 try-catch 处理
代码风格面向过程,使用函数进行内存管理面向对象,使用运算符进行内存管理,与类和对象结合紧密
  • C 语言的内存管理方式更偏向于底层和过程化,需要程序员手动处理很多细节,容易出错。
  • 而 C++ 的内存管理方式在类型安全性、对象生命周期管理和异常处理方面有很大改进,更适合开发大型、复杂的面向对象程序。
  • 不过,在一些性能敏感或者需要与 C 代码兼容的场景中,C 语言的内存管理方式仍然很有用。

以上就是这篇博客的全部内容,下一篇我们将继续探索C++中new和delete中更多精彩内容。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述


http://www.hkcw.cn/article/EmMXitHltX.shtml

相关文章

Educational Codeforces Round 175 (C.二分 D.树形结构、dp)

文章目录 2025.3.3C. Limited Repainting(二分)题意思路代码 D. Tree Jumps(树形结构、dp)题意思路代码 2025.3.3 Educational Codeforces Round 175 (Rated for Div. 2) C. Limited Repainting(二分) 题意 给出一个字符串a由“R”B“组成&#xff0c;不同位置对应一个惩罚…

老太突然倒地吓坏路人民警紧急相救 家属感激救命之恩

“谢谢你们帮我父亲‘捡’回一条命,再晚一会儿后果不堪设想!”5月21日,市民刘女士接到民警电话时再次表达了对紧急救援的感激之情。她的父亲76岁,患有阿尔兹海默病,下午扛着锄头出门后一直未归,家人找了3个多小时都没找到。5月19日傍晚6时许,大冶市公安局还地桥派出所接…

53岁男子诱骗侵害幼女被判刑 深挖彻查揭露更多罪行

5月29日,江苏省人民检察院召开新闻发布会,介绍了近年来加强未成年人网络司法保护的工作情况及典型案例。如皋市检察院副检察长卢海琴介绍了一起典型案例,通过深挖彻查,案件从1名被告人追到3名被告人、从1个罪名查到5个罪名、从1起强奸事实挖到19起犯罪事实、从1名被害人增加…

谷歌DeepMind最强手语翻译模型登场 打破沟通障碍

谷歌DeepMind团队于5月27日宣布推出SignGemma,这是其迄今为止最强大的手语翻译模型,能够将手语转化为口语文本。该开源模型计划在今年晚些时候加入Gemma模型家族。SignGemma支持多语言功能,但目前主要针对美国手语(ASL)和英语进行了深度优化,开发者可以自由使用并改进它。…

【C++】STL详解-----(二)vetor的使用

文章目录 vector的介绍vector的使用&#xff1a;元素访问empty vector的增删查改push_back和pop_backinsert和erase vector迭代器失效问题迭代器失效解决方法 vector的介绍 vector是可变大小数组的容器vector采用连续空间存储的方式&#xff0c;同时也表示可以采用下标访问vec…

string类

1. 为什么学习string类&#xff1f; string叫串&#xff0c;它是一个管理字符串的类&#xff0c;现实中为什么要出一个管理字符串的类呢&#xff1f;现实中我们有很多类型&#xff0c;比如int、double、char等&#xff0c;但发现这个世界的一些复杂东西都是通过字符串表示的…

潍坊烟花秀压轴项目缺席遭吐槽 设备故障引争议

多名网友在社交平台发帖表示,5月30日晚他们参加了潍坊世界风筝乐园的烟花秀表演,单人票198元,双人票298元。然而,之前宣传的压轴项目“七彩祥云”并未出现,引发观众不满。潍坊世界风筝乐园工作人员解释称,由于设备故障,“七彩祥云”环节被迫取消,并且当晚和接下来两天的…

江苏城市足球联赛为何这么火 赛事带动文旅热潮

最近,2025年江苏省城市足球联赛“苏超”火了。从“比赛第一,友谊第十四”到各地纷纷推出“跟着赛事游江苏”的文旅优惠,以足球为媒,以赛事为桥,江苏展现了独特的魅力。自5月10日揭幕以来,“苏超”迅速走红,成为江苏省乃至全国关注的热点。你以为“苏超”只是踢踢比赛?殊…

汪涵体验问界M9五座零重力座椅 舒适到不想起

汪涵体验了问界M9的零重力座椅,表情十分享受。这款座椅采用零压感知人体工学专利设计,腰部零压角为121,腿部零压角为136,能够使全身压力均匀分布,带来零压悬浮感。汪涵坐在上面久久不愿起身,甚至在车展主持时也选择躺着进行,这种舒适度让人非常心动。责任编辑:0882

德国为何要解除对援乌武器射程限制 西方军援策略重大转变

2025年5月28日,德国新当选总理弗里德里希默茨在柏林宣布,乌克兰的西方盟友将取消向基辅供应武器的射程限制。这一政策调整涵盖美国、英国、法国和德国等主要援乌国家,标志着西方对乌军援策略的重大转变,随即引发国际社会对俄乌冲突走向的强烈关注。在WDR组织的论坛上,默茨…

Nginx基础篇(Nginx目录结构分析、Nginx的启用方式和停止方式、Nginx配置文件nginx.conf文件的结构、Nginx基础配置实战)

文章目录 1. Nginx目录结构分析1.1 conf目录1.2 html目录1.3 logs目录1.4 sbin目录 2. Nginx的启用方式和停止方式2.1 信号控制2.1.1 信号2.1.2 调用命令 2.2 命令行控制2.2.1 基础操作类2.2.2 配置测试类2.2.3 进程控制类2.2.4 路径与文件类2.2.5 高级配置类 3. Nginx配置文件…

印军高层被追问是否与巴方会面 印空军将领打马虎眼

印巴停火协议签署后,两国都在宣称自己取得了胜利。然而,印度军方的表现却让人失望。5月11日,印度空军中将巴蒂召开记者会,面对记者们的追问,他声称印度空军在这次冲突中表现出色,并坚称打下了好几架巴基斯坦飞机。但当被问及具体数字时,他却表示“不想冒险猜测”,并解释…

大V:辽宁舰率海军最强编队驶入西太 展现惊人实力

自去年10月在南海与山东舰稍微展示了一下双航母的实力后,辽宁舰到今年5月中旬都没有什么动作,大家的目光都放在了更出色的山东舰和福建舰上。然而,辽宁舰在5月下旬南下西太平洋,展示了强大的实力。护航编队包括两艘055型驱逐舰、五艘052D型驱逐舰和三艘054A型护卫舰,再加上…

印度刚喊话巴基斯坦,转头联合蒙古军演?背后算盘藏不住了!

印度最近的一系列举动引起了广泛关注。不久前,印度还在与巴基斯坦紧张对峙,并放狠话威胁对方,紧接着又派了一个大型商业代表团访问台湾。更令人意外的是,印度现在绕过中国,直接与蒙古国进行联合军事演习。这种行为就像是小孩子打架,正面打不过就绕到背后捅手指头。然而,…

25艘龙船巡游广州荔湾湖 展现广府龙舟文化魅力

5月31日,第十五届“荔枝湾新西关”民俗文化活动“五月五龙船鼓”在广州荔湾湖公园盛大开幕。上午8时30分,十多艘来自南海盐步、坑口、茶滘等地的龙船装饰一新,从珠江口岸徐徐进入荔湾湖面,与泮塘村的龙船一同趁景。泮塘村的龙船作为东道主,率先引领着各兄弟村的龙船队伍绕…

河南鹤壁一水库水位下降现千佛石窟 千年佛像重见天日

近日,有网友发布视频显示,河南省鹤壁市淇县夺丰水库水位下降后,露出一处石洞。这处石洞虽然洞口不大,但内部却别有洞天,四周布满佛像,造型精细,栩栩如生。洞内还有较深的积水。不少网友称这个洞为千佛洞,并有人前来打卡。该石窟名为前嘴石窟,开凿于东魏时期,千百年来…

全世界都在划龙舟过端午 全球共庆文化盛宴

当农历五月的暖风拂过东亚的稻田,粽叶的清香飘荡在东南亚的街巷,龙舟的鼓点响彻欧美的河流,全世界共同庆祝源自中国的古老节日——端午节。这个绵延两千多年的传统节日,早已超越地域界限,成为人类共享的文化盛宴。在中国,从江南水乡到北国平原,家家户户清晨便飘起蒸煮粽…

美禁运C919发动机 破局之道在哪里 核心技术自主可控

没有发动机,中国的大飞机还能飞吗?当美国突然暂停向中国商飞出售航空发动机技术时,这个问题迅速引起广泛关注。西方媒体纷纷唱衰“C919即将搁浅”,而中国外交部则强硬回击,坚决反对这种恶意封锁。这场看似突如其来的问题,实际上揭示了中美科技竞争的深层次矛盾——中国航…

python调用C++ DLL

使用C创建动态链接库&#xff1a; dllmain.cpp #include <windows.h> #include <string> #include <vector> #include "opencv2/opencv.hpp"BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {return TRUE…

多米尼加坍塌事故死亡人数升至234人 全国哀悼日持续六天

当地时间5月31日,多米尼加医疗中心报告称,一名在俱乐部屋顶坍塌事故中的重症患者不幸去世,使得该事故的死亡人数上升至234人。这起事故发生在4月8日凌晨,地点是多米尼加首都圣多明各的一家俱乐部。当时俱乐部内正在举行知名歌手的演出,突然倒塌的屋顶导致许多人被埋在废墟…