C语言学习笔记——动态内存分配

1.内存模型

内存分区 说明
程序代码区(code area) 存放函数体的二进制代码
静态数据区(data area)
也称全局数据区,包含的数据类型比较多,如全局变量、静态变量、一般常量、字符串常量。其中:
  • 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
  • 常量数据(一般常量、字符串常量)存放在另一个区域。

注意:静态数据区的内存在程序结束后由操作系统释放。
堆区(heap area) 一般由程序员分配和释放,若程序员不释放,程序运行结束时由操作系统回收。malloc()calloc()free() 等函数操作的就是这块内存,这也是本章要讲解的重点。

注意:这里所说的堆区与数据结构中的堆不是一个概念,堆区的分配方式倒是类似于链表。
栈区(stack area) 由系统自动分配释放,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。

命令行参数区

存放命令行参数和环境变量的值,如通过main()函数传递的值。


2.动态内存分配和释放常用到的四个函数为:malloc()、calloc()、realloc() 和 free()。

注意:每个内存分配函数必须有相应的 free 函数,释放后不能再次使用被释放的内存,建议在 free 函数后把被释放指针置为 NULL,好处有二:

  • 再次访问该指针将出错,避免野指针;
  • 再次释放该指针不会让程序崩溃只是free函数失效。
注: 虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。new和delete是运算符,不是函数,因此执行效率高。
new运算符使用的一般格式为:
    new 类型 (初值);
用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。
delete运算符使用的一般格式为:
   delete [ ] 指针变量

3.内存泄露

有些时候,常常会有将内存丢失的情况,例如:

int *pOld = (int*) malloc( sizeof(int) );
int *pNew = (int*) malloc( sizeof(int) );

这两段代码分别创建了一块内存,并且将内存的地址传给了指针 pOld 和 pNew。此时指针 pOld 和 pNew 分别指向两块内存。

如果接下来进行这样的操作:
pOld=pNew;
pOld 指针就指向了 pNew 指向的内存地址,这时候再进行释放内存操作:
free(pOld);
此时释放的 pOld 所指向的内存空间就是原来 pNew 指向的,于是这块空间被释放掉了。但是 pOld 原来指向的那块内存空间还没有被释放,不过因为没有指针指向这块内存,所以这块内存就造成了丢失。

另外,你不应该进行类似这面这样的操作:
malloc( sizeof(int) );
这样的操作没有意义,因为没有指针指向分配的内存,无法使用,而且无法通过 free() 释放掉,造成了内存泄露。

4.可以通过C语言中的关键字来控制变量的存放区域;C语言共有 4 个关键字用来指明变量的存储类别:auto(自动的)、static(静态的)、register(寄存器的)、extern(外部的)。

变量类别

说明

auto

默认即auto类型

extern

1.在所有的代码块(函数、if 块、switch 块等)之外定义的变量称为全局变量,它的作用范围默认是整个程序,
也就是所有的源文件,包括 .c 和 .h 文件。
2.
虽然全局变量的作用范围是整个程序,但是如果希望在 a.c 中使用 b.c 中的变量,也必须先进行声明。声明
使用 extern 关键字
3.extern 变量的定义格式为:
  extern type name = value;
  声明格式为:
  extern type name;
4.extern 可以省略(我们通常就是这么做的),全局变量默认就是 extern 的
5.extern 变量的作用域跟它的声明位置有关,在代码块内声明的 extern 变量在代码块外无效。
6.函数和变量的声明有所不同,对于函数,你可以省略 extern

static

1.实际开发中我们一般将不需要被其他文件调用的全局变量或函数的作用范围限制在当前文件中,可以通过 static 关键字来限制
2.static 声明的变量称为静态变量,不管是全局变量还是局部变量,都存储在静态数据区(全局变量本来就存储在静态数据区,即使不加 static)。
静态数据区的数据在程序启动时就会初始化,直到程序运行结束;对于代码块中的静态局部变量,即使代码块执行结束,也不会销毁。
注意:静态数据区的变量只能初始化(定义)一次,以后只能改变它的值,不能再被初始化,即使有这样的语句,也无效。
3.

register

1.可以将使用频繁的变量放在CPU的通用寄存器中,寄存器的数量是有限的,通常是把使用最频繁的变量定义为 register 的。
2.关于寄存器变量有以下事项需要注意:
1) 为寄存器变量分配寄存器是动态完成的,因此,只有局部变量和形式参数才能定义为寄存器变量。
2) 局部静态变量不能定义为寄存器变量,因为一个变量只能声明为一种存储类别。
3) 寄存器的长度一般和机器的字长一致,所以,只有较短的类型如int、char、short等才适合定义为寄存器变量,诸如double等较大的类型,不推荐将其定义为寄存器类型。
4) CPU的寄存器数目有限,因此,即使定义了寄存器变量,编译器可能并不真正为其分配寄存器,而是将其当做普通的auto变量来对待,为其分配栈内存。当然,有些优秀的编译器,能自动识别使用频繁的变量,如循环控制变量等,在有可用的寄存器时,即使没有使用 register 关键字,也自动为其分配寄存器,无须由程序员来指定。


你可能感兴趣的:(C语言)