C/C++内存分配机制

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

C/C++内存分配机制

1.C语言中的内存机制

在C语言中,内存主要分为如下5个存储区:

(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。

(2)堆(Heap):由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收,但程序只要不结束,就有可能造成内存泄露。

(3)全局区/静态区(Global Static Area): 全局变量和静态变量存放区,程序一经编译好,该区域便存在。并且在C语言中初始化的全局变量和静态变量和未初始化的放在相邻的两个区域(在C++中,由于全局变量和静态变量编译器会给这些变量自动初始化赋值,所以没有区分了)。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。

(4)C风格字符串常量存储区: 专门存放字符串常量的地方,程序结束时释放。

(5)程序代码区:存放程序二进制代码的区域。
2.C++语言中的内存机制
在C++语言中,与C类似,不过也有所不同,内存主要分为如下5个存储区:

(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。

(2)堆(Heap):这里与C不同的是,该堆是由new申请的内存,由delete或delete[]负责释放。

(3)自由存储区(Free Storage):由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收。

(4)全局区/静态区(Global Static Area): 全局变量和静态变量存放区,程序一经编译好,该区域便存在。在C++中,由于全局变量和静态变量编译器会给这些变量自动初始化赋值,所以没有区分了初始化变量和未初始化变量了。需要说明一点,全局静态变量和局部静态变量都是存储在同一个静态区(全局区),只是作用域不同。

(5)常量存储区: 这是一块比较特殊的存储区,专门存储不能修改的常量(一般是const修饰的变量,或是一些常量字符串)。
3.堆和栈的区别

3.1  栈

具体的讲,现代计算机(冯诺依曼串行执行机制),都直接在代码低层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址(SS,堆栈段寄存器,存放堆栈段地址);有专门的机器指令完成数据入栈出栈的操作(汇编中有PUSH和POP指令)。

这种机制的特点是效率高,但支持数据的数据有限,一般是整数、指针、浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构(可以自定义栈结构支持多种数据类型)。因为栈的这种特点,对栈的使用在程序中非常频繁的 。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址入栈,然后跳转至子程序地址的操作,而子程序的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。

C/C++中的函数自动变量就是直接使用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因,因而要避免返回栈内存和栈引用,以免内存泄露。

3.2  堆

和栈不同的是,堆得数据结构并不是由系统(无论是机器硬件系统还是操作系统)支持的,而是由函数库提供的。基本malloc/calloc/realloc/free函数维护了一套内部的堆数据结构(在C++中则增加了new/delete维护)。

当程序用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间(常见内存分配算法有:首次适应算法、循环首次适应算法、最佳适应算法和最差适应算法等)。如果没有可用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回到内部堆结构中,可能会被适当的处理(比如空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。 这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下几个原因:

(1)系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分配来说会造成浪费。

(2)系统调用申请内存可能是代价昂贵的。 系统调用可能涉及到用户态和核心态的转换。

(3)没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

3.3  栈和堆的对比

从以上介绍中,它们有如下区别:

(1)栈是系统提供的功能,特点是快速高效,缺点是由限制,数据不灵活;而堆是函数库提供的功能,特点是灵活方便,数据适应面广,但是效率有一定降低。

(2)栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。

(3)栈空间分静态分配和动态分配,一般由编译器完成静态分配,自动释放,栈的动态分配是不被鼓励的;堆得分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/释放内存匹配是良好程序的基本要素。

(4)碎片问题:对于堆来讲,频繁的new/delete等操作势必会造成内存空间的不连续,从而造成大量的碎片,使程序的效率降低;对于栈来讲,则不会存在这个问题,因为栈是后进先出(LIFO)的队列。

(5)生长方向:堆的生长方向是向上的,也就是向这内存地址增加的方向;对于栈来讲,生长方向却是向下的,是向着内存地址减少的方向增长。

(6)分配方式:堆都是动态分配的,没有静态分配的堆;栈有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配则由alloca函数进行分配,但是栈的动态分配和堆不同,它的动态分配是由编译器进行释放,无需我们手工实现。

(7)分配效率:栈是机器系统提供的数据结构,计算机在底层提供支持,分配有专门的堆栈段寄存器,入栈出栈有专门的机器指令,这些都决定了栈的高效率执行。而堆是由C/C++函数库提供的,机制比较复杂,有不同的分配算法,易产生内存碎片,需要对内存进行各种管理,效率比栈要低很多。

4.实例分析

实例1:
看下面的一小段C程序,仔细体会各种内存分配机制。

int a = 0; //全局初始化区  

char *p1;  //全局未初始化区(C++中则初始化为NULL)

int main()  

{  

    int b;                  //b分配在栈上,整型  

    char s[] = "abc";       //s分配在栈上,char *类型;"abc\0"分配在栈上,运行时赋值,函数结束销毁

    char *p2;               //p2分配在栈上,未初始化  

    char *p3 = "123456";    //p3指向"123456"分配在字符串常量存储区的地址,编译时确定  p3存储在栈上

    static int c = 0;       //c在全局(静态)初始化区,可以多次跨函数调用而保持原值  

    p1 = (char *)malloc(10); //p1在全局未初始化区,指向分配得来得10字节的堆区地址  

    p2 = (char *)malloc(20); //p2指向分配得来得20字节的堆区地址 

    strcpy(p1, "123456");    //"123456"放在字符串常量存储区,编译器可能会将它与p3所指向的"123456"优化成一块

    return 0;

}  

实例2

看下面的一小段代码,体会堆与栈的区别:

int foo()

{

    //其余代码   

    int *p = new int[5];

    //其余代码 

    return 0;

}

其中的语句int *p = new int[5];就包含了堆与栈。其中new关键字分配了一块堆内存,而指针p本身所占得内存为栈内存(一般4个字节表示地址)。这句话的意思是在栈内存中存放了一个指向一块堆内存的指针p。在程序中先确定在堆中分配内存的大小,然后调用new关键字分配内存,最后返回这块内存首址,放入栈中。这段代码在VC6下的汇编代码为:

00401028    push         14h 

0040102A    call         operator new (00401060) 

0040102F    add          esp,4 

00401032    mov          dword ptr [ebp-8],eax 

00401035    mov          eax,dword ptr [ebp-8] 

00401038    mov          dword ptr [ebp-4],eax 

如果需要释放内存,这里我们需要使用delete[] p,告诉编译器,我要删除的是一个数组。

实例3

看下面的一小段代码,试着找出其中的错误:

#include <iostream>

using namespace std;

int main()

{

    char a[] = "hello";

    a[0] = 'X';

    cout << a << endl;

    char *p = "world";

    p[0] = 'X';

    cout << p << endl; 

    return 0;

}

发现问题了吗?是的,字符数组a的容量是6个字符,其内容为"hello\0"。a的内容时可以改变的,比如a[0]='X',因为其是在栈上分配的,也就是在运行时确定的内容。但是指针p指向的字符串"world"分配在字符串常量存储区,内容为"world\0",常量字符串的内容时不可以修改的。从语法上来说,编译器并不觉得语句p[0]='X'有什么问题,但是在运行时则会出现"access violation"非法内存访问的问题。

C++内存分配机制详解 C++ 内存分配机制,让大家在使用C++编程的时候防止内存泄露的问题 立即下载

相关推荐

c++内存分配机制

通过剖析malloc、free的底层实现机制,可以更加深刻的认识到它们如何进行合作,管理内存.

C++内存分配机制详解(附代码)

文章目录内存分配的过程:分配内存释放内存内存管理实例1实例2实例3 内存分配的过程: 分配内存 1.new() new是用于创建对象的函数,它通过调用operator new()来完成空间的分配,通过parameter new将对象在已分配的空间中创建。 2.operator new() 全局函数,它返回一个void*型的指针,该指针指向分配到的内存,operator new()通过调用malloc获得内存。 3.parameter new 它的使用方式为new(pointer)className(par

weixin_45189666的博客 1338

C++内存分配方式

内存分配方式有三种: 1、从静态存储区域分配。内存在程序编译的时候已经分配好了。这些内存在程序整个运行期间都存在,如全局变量,static变量等 2、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。如数组,局部变量,形参。 3、从上分配,亦称动态内存分配。程序在运行的时候用mallocnew申请任意多少的内存,程序员自己责在何时用freedelet...

lyc201219的博客 3039

C++程序内存的分配

栈的效率比要高很多,因为栈是机器系统提供的数据结构,计算机在底层提供了栈的支持,分配专门的寄存器来存放栈的地址,压栈和出栈都有相应的指令,因此栈在分配的效率上是一定比上快的。在执行函数时, 函数内局部变量的存储单元可以在栈上创建,函数执行结束的时候,这些内存单元会自动被释放,栈内存分配运算内置于处理器的指令集,效率高,但是 分配的内存容量有限。的生长方向是向上的,也就是向着内存地址增加的方向,而栈刚好是相反的,栈是向着内存减小的方向生长的(因为栈的空间十分有限,所以栈是从上限往栈的下限生长的)

机会是给有准备的人 2489

C++内存管理--详谈

1、简述 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃C++,转到Java者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能。本期专题将从内存管理、内存泄漏、内存回收这三个方面来探讨C++内存管理问题。 ..

locahuang的博客 2354

C++内存管理机制

内存五大分区 1.栈区 程序运行时分配内存c++程序运行时分配内存,主要存储函数的参数以及局部变量。 局部变量分配内存时在栈区从高地址到低地址查找空的内存区域进行分配。 函数运行结束时回收内存2.区 程序运行时分配内存 区又称动态内存分配区,主要存储new者malloc出来的内存,分配内存时在内存区中从低地址到高地址寻找空的内存区域进行分配。 delete者free后回收内存,如果没有delete者free操作则在程序运行结束后由操作系统回收内存。 3.全局区 程序编译时分配内存

weixin_45800628的博客 4527

C/C++中的内存分配机制

本文是结合了其它的几篇帖子

yixiubaixue的专栏 519

c/c++内存分配机制

1.C语言中的内存机制 在C语言中,内存主要分为如下5个存储区: (1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器责分配释放,函数结束,栈变量失效。 (2(Heap):由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收,但程序只要不结束,就有可能造成内存泄露。 (3)...

常胜将军的博客 462

C++】C/C++内存分配机制(非原创)

C语言在内存中一共分为如下几个区域,分别是: 内存栈区: 存放局部变量名; 内存区: 存放new者malloc出来的对象; 常数区: 存放局部变量者全局变量的值; 静态区: 用于存放全局变量者静态变量; 代码区:二进制代码。 知道如上一些内存分配机制,有助于我们理解指针的概念。 C/C++不提供垃圾回收机制,因此需要对中的数据进行及时销毁,防止内存泄漏,使用free和delete销毁new和malloc申请内存,而栈内存是动态释放。 全局变量、静态局部变量保存在全局数据区,初始化的和未初始

Anton的博客 594

C/C++内存分配机制(非原创)

原文链接:https://blog.csdn.net/jirryzhang/article/details/79518408 C语言在内存中一共分为如下几个区域,分别是: 1. 内存栈区: 存放局部变量名; 2. 内存区: 存放new者malloc出来的对象; 3. 常数区: 存放局部变量者全局变量的值; 4. 静态区: 用于存放全局变量者静态变量; 5. 代码区:二进制代码。 知道如...

lj779323436的博客 515

从GetMemory()看C/C++内存分配机制

先看下面一段代码 #include using namespace std; void GetMemory(char *p, int num) { p=new char[num]; } void main() { char *str=NULL; GetMemory(str,100); strcpy(str,"hello world!"); cout<<str<<endl; }

ysh512的专栏 701

C/C++ 内存分配 new 操作符:剖析new操作符的实现机制和使用技巧

C/C++ new操作符

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 2650

高效跨平台语音识别实战:whisper.cpp架构揭秘性能优化指南

在实时语音转录需求日益增长的今天,开发者面临着一个关键挑战:如何在保持高精度的同时,实现跨平台的高性能语音识别?whisper.cpp作为OpenAI Whisper模型的C/C++移植版本,通过其独特的架构设计解决了这一难题,为开发者提供了高效、轻量级的语音识别解决方案。 ## 技术痛点解决方案 ### 实时语音识别的性能瓶颈 传统语音识别系统在跨平台部署时常常面临计算资源限制、内存占用

gitblog_00375的博客 373

最全整理的C C++相关的面经知识点_c++ gdb 面经,7年老C C++一次操蛋的面试经历

为对象分配内存调用构造函数初始化这块内存在第一步中分配内存时就使用了operator new,其实现机制size=1;void *p;if(nh)nh();if(ptr)在第二步中,调用构造函数初始化内存,如果不涉及继承,这一步非常简单,就是给内存赋值,重点是operator new分配内存上,其处理流程如下:3. 分配内存如果成功则直接返回,否则;

2401_84976246的博客 477

【C/C++】深入理解C/C++内存分配

特性new/delete来源C 标准库函数C++ 操作符类型安全返回void*,需手动转换返回正确类型指针大小计算手动指定字节数自动计算构造/析构❌ 不调用✅ 自动调用失败处理返回 NULL抛出可重载❌✅内存来源 (heap)自由存储区 (free store)std::cout << "=== FILE* 自定义删除器 ===\n";if (f) {std::cout << "关闭文件\n";fclose(f);

数学、算法爱好者的博客 345

C++内存管理:new/delete vs malloc/free

C++new/deletemalloc/free的主要区别在于:new/deleteC++关键字,具有类型安全性和自动的构造/析构调用,失败时抛出异常,且支持运算符重载;而malloc/free是C标准库函数,无类型检查,需手动计算内存大小,仅进行内存分配/释放。C++引入new/delete是为了更好地支持面向对象特性(构造/析构)、类型安全和异常处理。free释放的内存通常归还内存管理器而非直接返还操作系统,后续malloc可能复用。最佳实践推荐在C++中使用智能指针和RAII模式管理资源。

弘羽 1094
Thanksgiveing
博客等级 码龄15年 0粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值