`
jackchen0227
  • 浏览: 146461 次
  • 性别: Icon_minigender_1
  • 来自: 帝都
社区版块
存档分类
最新评论

[zz] c 与 c++ 中的内存分配

    博客分类:
  • ACM
阅读更多

C语言跟内存分配方式
  (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

  (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

  (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多

 

  C语言跟内存申请相关的函数主要有 alloca,calloc,malloc,free,realloc,sbrk等.

  其中alloca(注意此处没有写错,是alloca )是向栈 申请内存,因此无需释放. malloc分配的内存是位于堆中 的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间.

  calloc则将初始化这部分的内存 ,设置为0. 而realloc则对malloc申请的内存进行大小的调整 .申请的内存最终需要通过函数free来释放. 而sbrk则是增加数据段的大小 ;

  malloc/calloc/free基本上都是C 函数库实现的,跟OS无关.C函数库内部通过一定的结构来保存当前有多少可用内存.如果程序malloc的大小超出了库里所留存的空间,那么将首先调用 brk系统调用来增加可用空间,然后再分配空间.free时,释放的内存并不立即返回给os,而是保留在内部结构中. 可以打个比方: brk类似于批发,一次性的向OS申请大的内存,而malloc等函数则类似于零售,满足程序运行时的要求.这套机制类似于缓冲.

  使用这套机制的原因: 系统调用不能支持任意大小的内存分配(有的系统调用只支持固定大小以及其倍数的内存申请,这样的话,对于小内存的分配会造成浪费; 系统调用申请内存代价昂贵,涉及到用户态和核心态的转换. 函数malloc()和calloc()都可以用来分配动态内存空间,但两者稍有区别。

 

  malloc()函数有一个参数,即要分配的内存空间的大小:

  Void *malloc(size_t size);

  calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小:

  void*calloc(size_t numElements,size_t sizeOfElement);

 

  原型 :extern void *realloc(void *mem_address, unsigned int newsize);

  语法: 指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。   
        头文件 :#include <stdlib.h > 有些编译器需要#include <alloc.h>,在TC2.0中可以使用alloc.h头文件   
        功能 :先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来   mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。   
        返回值 :如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。    
        注意:这里原始内存中的数据还是保持不变的 。当内存不再使用时,应使用free()函数将内存块释放。

 

     如果调用成功,函数malloc()和calloc()都将返回所分配的内存空间的首地址。
   malloc() 函数和calloc()函数的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中 的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开 始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题。

  calloc() 函数会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为零;如果你是 为指针类型的元素分配内存,那么这些元素通常(但无法保证)会被初始化为空指针;如果你是为实数类型的元素分配内存,那么这些元素可能(只在某些计算机 中)会被初始化为浮点型的零。

  malloc() 函数和calloc()函数的另一点区别是calloc()函数会返回一个由某种对象组成的数组,但malloc()函数只返回一个对象。为了明确是为一 个数组分配内存空间,有些程序员会选用calloc()函数。但是,除了是否初始化所分配的内存空间这一点之外,绝大多数程序员认 为以下两种函数调用方式没有区别:

  calloc(numElements ,sizeOfElement);

  malloc(numElements *sizeOfElement) ;

  需要解释的一点是,理论上(按照ANSIC标准)指 针的算术运算只能在一个指定的数组中进行,但是在实践中,即使C编译程序或翻译器遵循这种规定,许多C 程序还是冲破了这种限制。因此,尽管malloc()函数并不能返回一个数组,它所分配的内存空间仍然能供一个数组使用(对realloc()函数来说同 样如此,尽管它也不能返回一个数组)。

  总之,当你在calloc()函数和malloc()函数之间作选择时,你只需考虑是否要初始化所分配的内存空间,而不用考虑函数是否能返回一个数组。

  当程序运行过程中malloc了,但是没有free 的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,是的系统可用内存不 断减少.但是,内存泄漏仅仅指程序在运行时,程序退出时,OS将回收所有的资源.因此,适当的重起一下程序,有时候还是有点作用.

*/

在C++中,内存分成5个区,他们分别是堆、栈、自由存储区 、全局/静态存储区和常量存储区 。???

  栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

  堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

  自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

  全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

  常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)

  明确区分堆与栈


  在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。

  首先,我们举一个例子:

void f() { int* p=new int[5]; }

   这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就 是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator 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么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。

  好了,我们回到我们的主题:堆和栈究竟有什么区别?

  主要的区别由以下几点:

  1、管理方式不同;

  2、空间大小不同;

  3、能否产生碎片不同;

  4、生长方向不同;

  5、分配方式不同;

  6、分配效率不同;

  管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

  空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:

  打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。

  注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

   碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问 题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详 细的可以参考数据结构,这里我们就不再一一讨论了。

  生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

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

   分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率 比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在 堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会 分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

  从这里我们可以看到,堆和栈相比,由于大量new/delete的 使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是 应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而 不是用堆。

  虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

   无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果, 就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:)

 

分享到:
评论

相关推荐

    C/C++/VC项目含视频贪吃蛇游戏

    5. **动态内存管理**:在游戏过程中,随着蛇的增长,需要动态地分配和释放内存空间,以存储蛇的身体信息。 #### 五、项目实现步骤 1. **初始化游戏窗口**:设置游戏窗口的大小、背景颜色等基本属性。 2. **定义蛇...

    CString的工作原理介绍(zz).docx

    cstring,作为Microsoft MFC库中的重要成员,是C++对标准C字符串的封装,旨在解决C语言中字符串处理带来的诸多问题,如缓冲溢出和内存泄漏。这篇文章将深入探讨CString的工作原理,帮助开发者更好地理解和使用这一...

    zz.rar_hungarian algorithm_匈牙利_匈牙利算法

    【标题】"zz.rar_hungarian algorithm_匈牙利_匈牙利算法"涉及的核心知识点是匈牙利算法,这是一门在图论中的优化算法,主要用于解决匹配问题,特别是指二分图的最大匹配问题。在计算机科学和运筹学中,匈牙利算法...

    sendtrap

    3. MIB:MIB是SNMP中的数据结构,它定义了网络设备上的可管理对象,并为这些对象分配唯一的OID(Object Identifier),使得网络管理软件可以识别和操作它们。 4. SNMP Trap:当网络设备遇到异常情况时,例如硬件...

    c语言面试指导

    ### C/C++面试指导知识点详解 ...以上是C/C++面试中可能遇到的关键知识点。对于求职者来说,不仅要掌握这些基本概念,还需要具备解决实际问题的能力。通过不断地练习和实践,可以更好地应对面试中的各种挑战。

    windows动态链接文件

    这个库文件提供了开发人员在编写和调试C++程序时所需的运行时支持,包括异常处理、内存分配和释放等功能。 **msvcr120d.dll** 同样是Visual C++运行时库的一部分,它主要负责提供C运行时库功能,如数学运算、字符串...

    2024嵌入式大厂面经博彦科技笔试题

    - **内存管理**:学习内存分配与回收机制、内存保护技术等。 - **文件系统**:了解嵌入式系统中常用的文件系统类型及其特点。 #### 3. 编程语言及开发工具 - **C/C++编程**:掌握C/C++语言的基础语法和高级特性,如...

    2024嵌入式面试资料嵌入式整理笔试题

    - 使用静态内存分配;使用内存管理库;定期重启系统等。 ### 结论 通过对嵌入式系统的基础概念、编程语言、实时操作系统以及设计流程等方面的学习和理解,求职者可以更好地为即将到来的面试做好准备。同时,了解...

    2024嵌入式面试资料机试题2019大疆嵌入式笔试题A卷(附超详细解答)

    1. **C/C++**:由于其高效性和对底层硬件的直接访问能力,成为嵌入式开发中最常用的编程语言之一。 2. **Python**:虽然不如C/C++那样适用于低功耗设备,但在某些高性能嵌入式平台上仍得到广泛应用,特别是在机器...

    2024嵌入式大厂面经北京瑞德方科技

    - 内存管理:熟悉堆栈内存的区别、动态内存分配(malloc/free)和智能指针的使用。 - 高级特性:如模板、异常处理、命名空间等。 2. **操作系统基础知识**: - 实时操作系统(RTOS)原理及其与通用操作系统的...

    iosOC部分经典面试题大全总共26页题集

    根据给定的信息,我们可以深入探讨Objective-C中的一些关键概念及其面试题目解析,这些知识点对于理解和掌握Objective-C语言至关重要。 ### 1. Objective-C中的多重继承与多协议支持 #### 多重继承 Objective-C不...

    keil5——51

    4. **汇编器和链接器**:它们负责将源代码转换为可执行格式,并处理内存分配、符号解析等问题。 5. **仿真器和调试器**:内置的μVision调试器支持硬件和软件仿真,可以进行断点设置、变量观察、步进执行等功能。 ...

    算法参考资料ACM模板-复旦大学

    4. 编程语言规范:ACM竞赛通常要求使用C/C++或Java等语言。模板中可能会包含关于语言特性的介绍、标准库的使用说明、调试技巧等。 5. 时间和内存限制下的编程:由于ACM竞赛要求在严格的时间和内存限制下完成编程,...

    2024嵌入式面试资料嵌入式通信接口面试常用知识文档

    - **减少内存占用**:合理管理内存,避免不必要的内存分配和释放操作。 - **提高代码效率**:利用编译器提供的优化选项,合理使用函数内联等技巧。 - **确保代码可靠性**:采用良好的编码习惯,进行充分的测试和验证...

    2024嵌入式面试资料北京联发科嵌入式软件工程师笔试题目解析

    4. **内存管理**:动态内存分配、释放等。 5. **多线程编程**:线程同步机制、互斥锁等。 #### 选择题与判断题 1. **操作系统原理**:进程管理、内存管理等。 2. **编译原理**:预处理、编译、链接等过程。 3. **...

    2024嵌入式面试资料2010-2013华为历年笔试题大汇总

    - **内存管理**:理解堆栈的区别,动态内存分配函数的使用。 - **文件操作**:熟悉文件打开、关闭、读写等基本操作。 #### 2. 数据结构与算法 - **链表、队列、栈**等基本数据结构的操作。 - **排序算法**:如冒泡...

    2024嵌入式大厂面经傲天动联笔试面试题

    C/C++编程基础 - 指针操作 - 内存管理 - 数据结构和算法实现 #### 2. 操作系统原理 - 进程间通信(IPC) - 线程同步机制 - 内存管理和虚拟内存 #### 3. 计算机体系结构 - CPU架构(如ARM、MIPS) - 存储器层次...

    2024嵌入式面试资料华为历年笔试题大汇总(2)

    此外,对于求职者来说,掌握C/C++编程语言、操作系统原理以及数据结构与算法等基础知识至关重要,这些都是华为等公司笔试中的常见考点。希望以上的总结能帮助大家更好地准备面试,成功进入心仪的公司。

    算法参考资料AVR的PID技术

    2. **算法实现**:将PID算法以适当的编程语言(通常是C或C++)实现。在编程过程中,要考虑微控制器的处理能力、内存大小等因素。 3. **中断服务**:为确保PID控制的实时性,需要使用定时器中断来周期性地执行PID...

Global site tag (gtag.js) - Google Analytics