用完后释放存储器
既然有释放链表的函数,就需要在用完链表以后调用它。程序只需要显示链表的内容,显示完以后就可以释放它:
display(start);
release(start);
写完后你可以测试代码。
试驾
编译代码,运行程序并把文件传给它,看看发生了什么。
程序正确运行了。记住,你无法知道文件有多大。在这个例子中,即使你不把所有数据都保存在存储器中,也能把它们打印出来,但只有把数据放进存储器,才能自由地处理它们。你可以在旅途中添加或删除一些站点,还可以重新调整旅行的顺序,或扩充它。
有了动态分配存储器,就能在运行时创建需要的存储器。使用malloc()与free(),可以访问动态堆存储器。
今夜话题:栈与堆在讨论他们之间的差异
栈: | 堆: |
堆?你在家吗? | 平时这个时候很少见到你,最近忙啥呢? |
刚刚从一个函数返回,实在不好意思,最近一直在整理东西…… | 你都做了些什么? |
代码刚刚退出函数,我需要释放局部变量的空间。 | 你应该活得更简单一点,放轻松…… |
也许你是对的,我能坐下么? | 要啤酒吗?不用管这个帽子,扔一边就行了。 |
这个东西是你的吗? | 嘿,你找到了披萨!太好了,我找了它一个礼拜。 |
你应该让别人来帮忙收拾一下这里。 | 不用担心,在线点餐程序把披萨留在了这里,他应该还会回来的。 |
你怎么知道?万一他忘了呢? | 他重新联络过我,他调用了free() 。 |
嗯?你确定?这个程序是写“打兔子”游戏的那个家伙写的么?存储器泄漏得到处都是,满地的兔子结构,我都没法走路了。到处都是垃圾,太可怕了。 | 喂,清理存储器可不是我的责任。有人申请空间,我就给他,我会把空间留在那里,直到他叫我清理它。 |
你这样很不负责任。 | 也许吧,但使用起来很简单,不像你那么瞎折腾。 |
瞎折腾?我从来不瞎折腾!你可能想要一张纸巾…… | (打嗝)什么?我只是说你很难追踪。 |
你应该更合理地维护存储器。 | 随便,我一向宽以待人。如果程序想把存储器搞得乱七八糟,那也不是我的责任。 |
你真邋遢。 | 是随遇而安。 |
为什么你不做“垃圾收集”?! | 又来了…… |
一丁点儿整理工作而已,现在你什么也不干!!! | 放松。 |
(哭)对不起,我受不了了,这里实在是太乱了。 | 嘿,你的鼻子“溢出”了,用这个…… |
(擤鼻涕)谢谢,等一下,这是什么? | “打兔子”游戏的排行榜。别担心,我想程序不需要再用它了。 |
这里没有蠢问题
问:为什么“堆”要叫做“堆”?
答:因为计算机不会自动组织它,它只是一大“堆”数据而已。
问:什么是“垃圾收集(garbage colloection)”?
答:一些语言会跟踪你在堆上分配的数据,当你不再使用这些数据时,就会释放它们。
问:为什么C语言没有“垃圾收集”?
答:C语言非常古老,发明它的时候,绝大多数语言都没有自动“垃圾回收”机制。
问:这个例子中,复制island名字的原因我知道,但为什么
open
和close
的值不需要复制?答:
open
和close
的值都设为了字符串字面值,而字符串字面值无法更新,即便多项数据引用了相同字符串也没关系。问:
strdup()
函数实际上会调用malloc()
函数吗?答:这取决于C标准库是如何实现的,不过通常情况下,是这样的。
问:我需要在程序结束前释放所有数据吗?
答:不必,操作系统会在程序结束时清除所有存储器。不过,你还是应该显式释放你创建的每样东西,这是一种好的习惯。
要点
可以用动态数据结构保存可变数量的数据项。
可以很方便地在链表这种数据结构中插入数据项。
在C语言中,动态数据结构通常用递归结构来定义。
递归结构中有一个或多个指向相同结构的指针。
栈用来保存局部变量,它由计算机管理。
堆用来保存长期使用的数据,可以用
malloc()
分配堆空间。
sizeof
运算符告诉你一个结构需要多少空间。数据会一直留在堆上,直到用
free()
释放它。
你已经学会了用C语言创建链表,但链表并不是唯一的数据结构,可能还需要创建其他数据结构。下面是一些其他数据结构的例子,看你能不能把数据结构与使用说明连起来。
你已经学会了用C语言创建链表,但链表并不是唯一的数据结构,可能还需要创建其他数据结构。下面是一些其他数据结构的例子,你将把数据结构与使用说明连起来。
数据结构很有用,但要小心使用!
当用C语言创建这些数据结构时需要非常小心,如果没有记录好保存的数据,就很可能把不用的数据留在堆上。时间一久,它们就开始消耗机器上的存储器,程序也可能因为存储器错误而崩溃。所以,你必须学会如何追查代码中的存储器泄漏,并学会如何修复它们……