这几天在实验室写代码,从以前习惯的 Java 环境一下子回到了纯 C 语言的时代,有点不太适应。不过 C 语言确实是最灵活最强大的,简直就像乾坤大挪移,内功不够的人只能徒然兴叹,纵如杨逍之类的绝世高手也不过习得入门皮毛。——所以,先修内功~~
C 语言和 Java 一个很明显的不同,就是需要程序员自己来手动释放内存,所以刚开始回到 C 环境的时候,确实被内存泄露搞得焦头烂额。为了不在程序里埋下隐患,我决定还是仔细学习一下系统内存的动态分配和释放的一些知识吧。
内存的管理,这个当然是操作系统的事,如果是个开源的操作系统,比如UNIX,我们还可以修改内存管理的行为。但是操作系统是怎么来管理内存的呢?我刚开始以为这是个很简单的事情,比如我想申请 100 个 char 的空间,于是 char *p = (char *) malloc(100*sizeof(char)) 得到指向这块内存首地址的指针,要是用完了,直接 free(p),完事。——虽然事实上差不多就是这样的,但是这个过程中的很多细节问题我从来没有想过。
首先一个最直接的问题,我们都知道 C 语言是没有函数重载的,也就是说函数的参数类型是唯一的,但是 free 函数接收的参数却可以是任意类型的指针,为什么呢?——当然这个问题很简单,因为 free 函数接收的参数类型是 void * 指针,任何其他类型的指针都可以转化为 void * 指针,所以 free 函数可以释放任意指针指向的内存。
说到这里就不得不说一下指针类型,为什么在使用的时候一定要指定指针类型呢?因为使用的时候需要知道指针指向实际内容的大小,一个 char * 就指向一个 1B 的空间,一个 long * 就指向一个 4B 的空间,这样在移动指针的时候才能够移到正确的位置。比如一个 char *p,那么 p++ 就将指向下一个 Byte,如果是 long *p,那么 p++ 就指向 4Btye 之后的地址。
新的疑问又来了,既然 free 函数接收的是一个 void * 指针,那他怎么知道到底该释放多少内存?
事实上,系统在管理、分配内存的时候,并不简单地将一块空内存的首地址返回给你,而是记录了一点额外的信息。事实上,在你用 malloc 申请的内存地址前面,系统自己附了一个结构体,用来记录一些管理信息,比如这块内存块的大小,下一个可用的内存块的地址……啊等等等等。这些东西当然是不会给你看到的,所以系统就放在返回给你的内存块的前面,所以你得到的指针指向的就是一块空白的内存。
利用这个额外的信息,那么释放内存就变得容易了,在 free 函数里,根据接收到的指针得到需要释放的内存块的首地址,然后往前倒退几个字节,找到那些额外的信息,根据这些信息,很容易就能知道应该释放多少内存。从这点上来说,即使你将一个 char * 强制转换成了 long * 那么在使用 free 释放的时候也一样只是释放了 1B 的大小。
以上只是一个简单的笔记,给自己留一个备份,主要是记录一些想法,有些地方欠缺严谨。再加上我对这块内容也是了解不够,因此只能说与大家分享吧,也希望有牛人来指点指点,一起讨论。
references:
http://topic.csdn.net/u/20081107/17/a0e8e8d0-1b30-459e-a640-53966f3e7640.html
http://topic.csdn.net/u/20090714/07/9ee25af0-8ecf-42e9-9ae5-ec2bdd1f72b8.html
http://blog.chinaunix.net/u3/93713/showart_2113389.html
http://g.oswego.edu/dl/html/malloc.html