C语言之内存分配

在C语言的运行过程中,需要内存来存储数据。C语言使用的内存总体可以分为两类:一类是静态区,一类是动态区。


静态数据存储区包含:只读数据区、已初始化的读写数据区、未初始化的读写数据区。

动态数据存储区包含:栈内存区域、堆内存区域
C语言之内存分配
C语言编写的程序经过编译-连接之后,将形成一个统一的文件,它由几个部分组成:
代码段(code或text)、只读数据段(RO data)、已初始化读写数据段(RW data)、未初始化读写数据段(BSS)、堆(heap)、栈 (stack)

代码段:


由各个函数产生,函数的每一条语句最终都生成机器码,例如普通的数学运算、条件语句、循环语句

只读数据段:


由程序使用的数据产生,该部分数据的特点是不需要修改,因此被放入只读部分,例如:
只读全局变量 const char a[10] = “hello!!!”;
只读局部变量 const char str[8] = “world”;
程序中使用的常量 printf(“ni hao c\n”); “ni hao c\n”就被放入只读数据

读写数据段:


又称为已初始化数据段,它里面的变量可读可写,例如:

未初始化数据段:


该段的数据没有初始化,它只在目标文件中被标识,并不会称为一个真正的段。只有在程序运行的初始化阶段才会产生,因此不会影响目标文件大小

栈:


只在程序运行的过程中出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间
栈的特点是先进后出
栈的增长方式有2种:一种是向上增长的,即从低地址向高地址增长;一种是向下增长的,即从高地址向低地址增长。目前绝大多是系统是递减的
根据栈的指针位置又可以讲栈分为满栈和空栈。
C语言之内存分配
C语言之内存分配
C语言之内存分配
C语言之内存分配
按照满栈的处理情况:入栈的时候,入栈内容放在栈指针移动后的内存地址;出栈的时候,将弹出当前栈指针地址的内容
按照空栈的处理情况:入栈的时候,入栈内容放在当前栈指针的内存地址中;出栈的时候,将弹出栈指针移动后地址的内容

堆:


只在程序运行的过程中出现,一般由程序员分配和释放

动态内存分配:


1、new操作符和malloc函数
C++中的new操作符会隐式的考虑到分配的数据类型(与delete配合使用),表达式实例(以int类型为例)

new int[4]

malloc函数向系统申请一块指定大小的连续内存块(与free配合使用),需要一个参数来指定分配空间的字节大小,函数原型为

void* malloc(unsigned size)

表达式实例

malloc(4*sizeof(int))

malloc函数在内存的堆区中查找符合要求的内存块,若成功则做好标记表示已分配并返回指向该内存块起始字节的指针(不清理该内存块)否则返回NULL,因此使用其分配的内存前应检查该指针的有效性。其中指针有效性可用断言assert,其本质上是一种宏而非函数。它以一个布尔值为参数,然后实现类似测试的功能,如果测试结果为真,它实际上什么都不做,如果结果为假,assert会终止程序运行,然后提示程序终止的位置,此时编译器会提示包含执行代码的文件号以及终止的assert语句的行号。(基本思想:在继续执行断言以后的操作时,保证某些条件的值为真)。

拓展总结

特征 new/delete malloc/free
分配内存的位置 *存储区 堆
内存分配成功的返回值 完整类型指针 void*
内存分配失败的返回值 默认抛出异常 返回NULL
分配内存的大小 由编译器根据类型计算得出 必须显式指定字节数
处理数组 有处理数组的new版本new[] 需要用户计算数组的大小后进行内存分配
已分配内存的扩充 无法直观地处理 使用realloc简单完成
是否相互调用 可以,看具体的operator new/delete实现 不可调用new
分配内存时内存不足 客户能够指定处理函数或重新制定分配器 无法通过用户代码进行处理
函数重载 允许 不允许
构造函数与析构函数 调用 不调用

2、calloc函数
calloc函数与malloc函数的主要差别是函数原型不同,calloc函数原型为

void* calloc(unsigned nitems, unsigned size)

其功能是分配nitems个连续的内存块,每块字节数为size。由于calloc函数实质上是分配nitems*size个字节的连续内存块,因此,calloc(nitems, size)与malloc(nitems*size)功能上等同。不同于malloc函数,处理细节上,calloc函数将所分配的内存块清零。

3、realloc函数
函数原型

void* realloc(void *block, unsigned size)

用于动态申请内存后,空间的大小调整,该函数将释放和重新分配内存块在一次调用中完成,参数block是被释放的内存块的起始地址,参数size说明新申请的字节数,细节上,函数先判断是否可在原申请的内存空间上拓展成符合重新分配要求的新空间,若可,则执行;否则,函数在堆区中寻找符合重新分配大小的空间,若找到,则拷贝原空间存储信息的位模式到新空间,返回向该内存块起始字节的指针释放原空间,如果未找到符合要求的空间,realloc返回NULL不释放原空间。