(本文基于linux系统。)
首先说一下程序运行是的存储分配:
这张是典型的C语言的存储分配图。动态存储分配主要涉及图中的堆区。堆是无结构的连续的存储区域。当调用malloc()函数时,存储分配器从堆中找一块合适大小的连续的内存空间返回给程序。
malloc和free函数的原型如下:
void * malloc(size_t size)
void free(void* ptr)
这两个函数的使用就不在赘述了。
其实说白了,malloc和free函数就是负责对堆区的存储空间进行管理。既然要对堆区进行管理,而堆区又是无结构的,因此需要一个(运行库)自定义的结构来对堆区进行管理。
既然是对存储空间的管理,那么必然要面对两个问题--------存储空间的利用率和运行的效率。
首先是存储空间的利用率,这里面牵扯到一个概念--------碎片(fragmentation)。碎片又分为两种:内碎片和外碎片。
- 内碎片是指分配给程序的存储空间没有用完,有一部分是闲着的,这部分空间成为内碎片。
- 外碎片是虽然还有空间可以分配,但是这些空间都是不连续的小块,不能满足程序对连续存储的要求,因此无法分配给程序使用。这些不能满足程序要求的小的可以分配的存储空间成为外碎片。
对内碎片的处理很容易,只要保证分配的空间和使用的空间一样大小就行了。这主要是靠写程序的程序员们估算好自己所需要的空 间。(为了保证分配的空间是块对齐,也就是双字对齐,分配器可能返回比实际申请的空间多出一个字节的空间。但相对于目前庞大的内存空间,这一个字节已经微 不足道了。但是如果程序申请大量的小块内存,这个问题还是要考虑的。。。比如:大量申请3个字节的空间,为了对齐,返回四个字节。这就导致四分之一的空间 浪费。)
对于外碎片,处理的方法说起来很简单--------合并。但说归说,真正合并的时候就很困难。如果空闲的碎片是连续的, 那合并很简答。如果碎片不连续,这就很麻烦了。可以对内存进行紧缩,但这样效率很难保证。操作系统对内存进行管理是,大都使用分页,分段或段页式的管理来 减少外碎片。如果在程序中对堆区的管理再引入复杂的外碎片处理措施,不但运行效率低,而且多此一举。。。所以,当在处理合并的时候,基本都只考虑相邻的空 闲块。
关于效率的问题,后面会详细说明。
如果空间无限大,那么malloc就可以不断的分配新的空间给程序,而free函数则什么都不要做。因此,使用过的空间将直接丢弃而不再重用。这样 malloc和free函数即简单,效率也高。但是!存储空间是有限的。理论上在 32-位 x86 系统上,进程可用的最大空间是4G(由地址宽度决定)。但是不可能直接给程序分配4G的空间。。。因此,程序一开始运行的时候,堆区都有一个默认的大小。 这样,在程序运行的过程中,可能出现堆区中的空间都耗尽的情况(也可能没有耗尽,但都是小的碎片)。因此,就要给程序的堆区扩容,也就是让操 作系统给程序分配更多的空间,术语叫把空间映射到程序的堆区中。在这里有一个概念,叫系统中断点或当前中断点,也就是被映射的内存的边界(最后一个有效地 址),也就是堆顶。
对堆区进行扩容,可调用下面的两个系统调用:
- brk:
int brk(void *end_data_segment)
是一个非常简单的系统调用。 brk()
只是简单地将系统中断点设置为end_data_segment,向进程添加内存或者从进程取走内存。
- mmap:
mmap()
,或者说是“内存映像”,类似于 brk()
,但是更为灵活。首先,它可以映射任何位置的内存, 而不单单只局限于进程。其次,它不仅可以将虚拟地址映射到物理的 RAM 或者 swap,它还可以将 它们映射到文件和文件位置,这样,读写内存将对文件中的数据进行读写。不过,在这里,我们只关心 mmap
向进程添加被映射的内存的能力。 munmap()
所做的事情与mmap()
相反。
还有一个叫sbrk的系统调用void *sbrk(intptr_t increment)。sbrk()增加increment字节的内存到进程的堆区。而其返回的是旧的系统中断点,而不是新的。
下面说一下一种叫做隐式空闲链表的管理堆区的方法。堆块的格式如下:
-------------------------------------------------------------------
| int available /*是否空闲*/ |
| int size /*此块的大小*/ |
------------------------------------------------------------------
| | <----malloc返回的指针指向此处
| 实际空闲内存区 |
| |
------------------------------------------------------------------
| 填充区(可选) |
------------------------------------------------------------------
available 表示此块是否可用(被分配)。为1时表示可以分配,为0时表示被占用。
size表示此块内存的大小。
填充区用来进行块对齐(双字对齐)。
当调用malloc进行内存分配的时候,需要从空闲的内存块中选择一块合适的。选择的方法常用的有三种:首次匹配,下一次匹配,最佳匹配。
首次匹配:从头开始搜索表,选择第一个合适的块。
下一次匹配:和首次匹配类似,但是不是每次都从头搜索,而是从上一次搜索结束的位置开始搜索。
最佳匹配:选择所有合适的空闲块中最小的一块。
下一次匹配的运行速度要比首次匹配快,但空间利用率没有首次匹配高。最佳匹配的空间利用率最高,但运行也是最慢的。
当搜索到合适的空闲块后,这个空闲块往往不会整好合适,基本上都会大一下。多余的部分如果也分配给程序,就造成了内碎片。如果不分配,就要对空闲块进行分割。将多余的部分重新组成一个空闲块。这就有可能造成外碎片。因此,到底怎样处理,要看运行库的实现了。
若两个空闲块整好是连着的,那么把这两个块合并成一个块就可以提高空间的利用率。如果当前块的后一个块是空闲块,那么根据当前块的指针和其长度,很容易得到下一个空闲块的位置指针。但当空闲块在当前块的前面时,就很难获得其指针了。Knuth提出了一种聪明而有效的方法,叫做边界标记。就是复制块头到空闲块的尾部,形成一个块尾。这样,只需将当前块的指针向前移动一个块头大小的距离,就可以得到指向前一个块的块尾的指针,这样,前一个块的所有信息就都得到了。
在实际的实现中,往往引入两个特殊的块------序言块和结尾块。
序言块是程度为零且一直标记为不可分配的块,在整个堆区的最下面(堆区的开始位置)。序言块有块头和块尾。
结尾块在堆区的最上面(堆区的结束位置),也是长度为零且一直为不可分配。结尾块只有块头。
这两个块的引入可以减少很多边界的处理,提高程序的简洁性和效率。
下面是一个简单的实现的例子:
采用首次匹配。
1
/*
2
* File: my_alloc.h
3
*/
4
5
#ifndef _MY_ALLOC_H
6
#define
_MY_ALLOC_H
7
8
#ifdef __cplusplus
9
extern
"
C
"
10
{
11
#endif
12
13
#include
<
unistd.h
>
14
#define
INIT_S 10240
//
堆区的默认大小
15
int
is_initialized
=
0
;
//
标记是否已经初始化
16
void
*
memory_start;
//
堆区的开始地址
17
void
*
memory_end;
//
系统中断点
18
19
/*
20
* 内存控制块
21
* 块头和块尾的结构
22
*/
23
typedef
struct
__mem_control_block
24
{
25
int
available;
//
是否可用
26
int
size;
//
大小
27
} mem_control_block;
28
29
/*
30
* 初始化堆区。
31
* 建立堆区的初始结构,设置序言块,结尾块等。
32
*/
33
void
my_malloc_init()
34
{
35
/*
36
* 获得系统中断点,也即堆区的堆顶
37
*/
38
memory_end
=
sbrk(
0
);
39
/*
40
* 堆区的开始位置
41
*/
42
memory_start
=
memory_end;
43
/*
44
* 为堆区预先分配INIT_S个字节。
45
*/
46
if
(sbrk(INIT_S)
<=
0
)
47
{
48
printf(
"
Init Error!\n
"
);
49
return
;
50
}
51
memory_end
+=
INIT_S;
52
53
mem_control_block
*
mcb;
54
/*
序言块
*/
55
/*
块头
*/
56
mcb
=
memory_start;
57
mcb
->
available
=
0
;
58
mcb
->
size
=
0
;
59
/*
块尾
*/
60
mcb
=
(
void
*
)mcb
+
sizeof
(mem_control_block);
61
mcb
->
available
=
0
;
62
mcb
->
size
=
0
;
63
64
/*
设置控制块, 块头
*/
65
mcb
=
memory_start
+
2
*
sizeof
(mem_control_block);
66
mcb
->
available
=
1
;
67
/*
这个块的长度为整个堆区(除去控制块的长度)
*/
68
mcb
->
size
=
INIT_S
-
5
*
sizeof
(mem_control_block);
69
/*
设置边界标记,块尾
*/
70
mcb
=
memory_end
-
2
*
sizeof
(mem_control_block);
71
mcb
->
available
=
1
;
72
mcb
->
size
=
INIT_S
-
5
*
sizeof
(mem_control_block);
73
74
75
/*
结尾块
*/
76
mcb
=
memory_end
-
sizeof
(mem_control_block);
77
mcb
->
available
=
0
;
78
mcb
->
size
=
0
;
79
80
/*
81
* 已经进行了初始化
82
*/
83
is_initialized
=
1
;
84
}
85
86
/*
87
* 模拟malloc函数
88
*/
89
void
*
my_malloc(
int
size)
90
{
91
if
(
!
is_initialized)
92
{
93
my_malloc_init();
94
}
95
96
/*
分配的内存块的地址
*/
97
void
*
mem_location
=
0
;
98
99
/*
内存控制块的指针
*/
100
mem_control_block
*
curr_mcb,
*
tail;
101
102
/*
从头开始遍历
*/
103
curr_mcb
=
memory_start;
104
105
while
((
void
*
)curr_mcb
<
memory_end)
106
{
107
if
(curr_mcb
->
available)
/*
找到一个空闲块
*/
108
{
109
if
(curr_mcb
->
size
>=
size
&&
curr_mcb
->
size
<
size
+
2
*
sizeof
(mem_control_block))
110
/*
大小合适,多余的空间不够进行分割的。也就是剩余的空间无法满足块头和块尾所需要的空间。
*/
111
{
112
/*
获得返回的内存地址
*/
113
/*
114
* 此处必须把curr_mcb转成void*的格式!!
115
* 否则,在加的时候是以sizeof(mem_control_block)的倍数增加,而不是仅仅加一!!
116
*/
117
mem_location
=
(
void
*
) curr_mcb
+
sizeof
(mem_control_block);
118
/*
标记已经占用
*/
119
curr_mcb
->
available
=
0
;
120
121
/*
获得边界标记的指针,块尾
*/
122
tail
=
(
void
*
) curr_mcb
+
sizeof
(mem_control_block)
+
curr_mcb
->
size;
123
124
/*
标记已经占用
*/
125
tail
->
available
=
0
;
126
break
;
127
}
128
else
if
(curr_mcb
->
size
>
size
+
2
*
sizeof
(mem_control_block))
129
/*
进行分割
*/
130
{
131
int
old_size
=
curr_mcb
->
size;
132
/*
获得分配的内存块的地址
*/
133
mem_location
=
(
void
*
) curr_mcb
+
sizeof
(mem_control_block);
134
/*
标记已经占用
*/
135
curr_mcb
->
available
=
0
;
136
curr_mcb
->
size
=
size;
137
/*
获得边界标记的指针,块尾
*/
138
tail
=
(
void
*
) curr_mcb
+
sizeof
(mem_control_block)
+
size;
139
/*
标记已经占用
*/
140
tail
->
available
=
0
;
141
tail
->
size
=
size;
142
143
/*
将余下的部分分割成新的空闲块
*/
144
mem_control_block
*
hd,
*
tl;
/*
新块的块头和块尾
*/
145
/*
块头
*/
146
hd
=
(
void
*
) tail
+
sizeof
(mem_control_block);
147
hd
->
available
=
1
;
148
hd
->
size
=
old_size
-
size
-
2
*
sizeof
(mem_control_block);
149
/*
块尾
*/
150
tl
=
(
void
*
) hd
+
hd
->
size
+
sizeof
(mem_control_block);
151
tl
->
available
=
1
;
152
tl
->
size
=
hd
->
size;
153
154
break
;
155
}
156
}
157
158
/*
指向下一个块
*/
159
curr_mcb
=
(
void
*
) curr_mcb
+
curr_mcb
->
size
+
2
*
sizeof
(mem_control_block);
160
}
161
162
/*
没有找到合适的块,则扩展堆区,分配合适大小的内存加到堆区
*/
163
if
(
!
mem_location)
164
{
165
/*
申请空间
*/
166
if
(sbrk(size
+
2
*
sizeof
(mem_control_block))
<=
0
)
167
{
168
printf(
"
Sbrk Error!\n
"
);
169
return
0
;
170
}
171
/*
设置控制块的信息
*/
172
curr_mcb
=
(
void
*
)memory_end
-
sizeof
(mem_control_block);
173
curr_mcb
->
available
=
0
;
174
curr_mcb
->
size
=
size;
175
/*
设置边界标记,块尾的信息
*/
176
tail
=
(
void
*
) curr_mcb
+
curr_mcb
->
size
+
sizeof
(mem_control_block);
177
tail
->
available
=
0
;
178
tail
->
size
=
size;
179
180
/*
获得分配的内存块的地址
*/
181
mem_location
=
(
void
*
)curr_mcb
+
sizeof
(mem_control_block);
182
183
memory_end
=
memory_end
+
size
+
2
*
sizeof
(mem_control_block);
184
/*
设置结尾块
*/
185
tail
=
(
void
*
)memory_end
-
sizeof
(mem_control_block);
186
tail
->
available
=
0
;
187
tail
->
size
=
0
;
188
}
189
190
return
mem_location;
191
}
192
/*
193
* 模拟free函数
194
*/
195
void
my_free(
void
*
ptr)
196
{
197
if
(ptr
<=
0
)
198
{
199
return
;
200
}
201
202
mem_control_block
*
curr;
203
/*
指向控制块的地址
*/
204
curr
=
ptr
-
sizeof
(mem_control_block);
205
206
207
208
/*
标记为空闲,可用
*/
209
curr
->
available
=
1
;
210
/*
211
* 合并
212
*/
213
mem_control_block
*
pre,
*
next,
*
tmp;
214
215
/*
获得前一个块的块头地址
*/
216
pre
=
ptr
-
2
*
sizeof
(mem_control_block);
/*
这条语句获得了前一个块的块尾的地址
*/
217
pre
=
(
void
*
) pre
-
pre
->
size
-
sizeof
(mem_control_block);
/*
进一步计算块头的地址
*/
218
219
/*
获得后一个块的块头地址
*/
220
next
=
ptr
+
curr
->
size
+
sizeof
(mem_control_block);
221
222
if
(
!
pre
->
available
&&
next
->
available)
/*
只有后一个块空闲
*/
223
{
224
curr
->
size
+=
(next
->
size
+
2
*
sizeof
(mem_control_block));
225
/*
设置块尾
*/
226
tmp
=
(
void
*
) curr
+
curr
->
size
+
sizeof
(mem_control_block);
227
tmp
->
available
=
1
;
228
tmp
->
size
=
curr
->
size;
229
}
230
else
if
(pre
->
available
&&
!
next
->
available)
/*
只有前一个块空闲
*/
231
{
232
pre
->
size
+=
(curr
->
size
+
2
*
sizeof
(mem_control_block));
233
/*
设置块尾
*/
234
tmp
=
(
void
*
) pre
+
pre
->
size
+
sizeof
(mem_control_block);
235
tmp
->
available
=
1
;
236
tmp
->
size
=
pre
->
size;
237
}
238
else
if
(pre
->
available
&&
next
->
available)
/*
前后都块空闲
*/
239
{
240
pre
->
size
+=
(curr
->
size
+
4
*
sizeof
(mem_control_block)
+
next
->
size);
241
/*
设置块尾
*/
242
tmp
=
(
void
*
) pre
+
pre
->
size
+
sizeof
(mem_control_block);
243
tmp
->
available
=
1
;
244
tmp
->
size
=
pre
->
size;
245
}
246
247
return
;
248
249
}
250
251
void
print_info()
252
{
253
printf(
"
printf_info:\n
"
);
254
mem_control_block
*
curr
=
memory_start;
255
while
((
void
*
)curr
<
memory_end)
256
{
257
258
printf(
"
a? %d s: %d %d %d\n
"
, curr
->
available, curr
->
size, memory_end, curr);
259
curr
=
(
void
*
) curr
+
curr
->
size
+
2
*
sizeof
(mem_control_block);
260
}
261
}
262
263
264
#ifdef __cplusplus
265
}
266
#endif
267
268
#endif
/* _MY_ALLOC_H */
269
270
在真正的实现中,不可能使用上面程序中的结构体来表示控制块(块头,块尾)。这样虽然简单但很浪费空间。实际中一般都是位来表示,这样节省空间,但是对编程要求细致入微,来不得半点马虎。上面的例子仅仅是模拟一下。有兴趣的读者可以看看gun libc的代码。
隐式空闲链表的实现简单,但效率较低。因为运行的时候需要很多顺寻查找。实际的运用中可以使用分离的空闲链表或显示空闲链表等方法。有兴趣的读者可以参考《深入理解计算机系统》Randal E.Bryant David O'Hallaron 中国电力出版社的汉译版 第十章 10.9节动态存储分配。
好了,就写这么多。如有错误还请各位读者指正。
参考资料:《深入理解计算机系统》Randal E.Bryant David O'Hallaron 著 中国电力出版社
1 /*
2 * File: my_alloc.h
3 */
4
5 #ifndef _MY_ALLOC_H
6 #define _MY_ALLOC_H
7
8 #ifdef __cplusplus
9 extern "C"
10 {
11 #endif
12
13 #include <unistd.h>
14 #define INIT_S 10240 //堆区的默认大小
15 int is_initialized = 0; //标记是否已经初始化
16 void *memory_start; //堆区的开始地址
17 void *memory_end; //系统中断点
18
19 /*
20 * 内存控制块
21 * 块头和块尾的结构
22 */
23 typedef struct __mem_control_block
24 {
25 int available; //是否可用
26 int size; //大小
27 } mem_control_block;
28
29 /*
30 * 初始化堆区。
31 * 建立堆区的初始结构,设置序言块,结尾块等。
32 */
33 void my_malloc_init()
34 {
35 /*
36 * 获得系统中断点,也即堆区的堆顶
37 */
38 memory_end = sbrk(0);
39 /*
40 * 堆区的开始位置
41 */
42 memory_start = memory_end;
43 /*
44 * 为堆区预先分配INIT_S个字节。
45 */
46 if (sbrk(INIT_S) <= 0)
47 {
48 printf("Init Error!\n");
49 return;
50 }
51 memory_end += INIT_S;
52
53 mem_control_block *mcb;
54 /*序言块*/
55 /*块头*/
56 mcb = memory_start;
57 mcb -> available = 0;
58 mcb -> size = 0;
59 /*块尾*/
60 mcb = (void*)mcb+ sizeof(mem_control_block);
61 mcb -> available = 0;
62 mcb -> size = 0;
63
64 /*设置控制块, 块头*/
65 mcb = memory_start + 2 * sizeof(mem_control_block);
66 mcb -> available = 1;
67 /*这个块的长度为整个堆区(除去控制块的长度)*/
68 mcb -> size = INIT_S - 5 * sizeof (mem_control_block);
69 /*设置边界标记,块尾*/
70 mcb = memory_end - 2 * sizeof (mem_control_block);
71 mcb -> available = 1;
72 mcb -> size = INIT_S - 5 * sizeof (mem_control_block);
73
74
75 /*结尾块*/
76 mcb = memory_end - sizeof (mem_control_block);
77 mcb -> available = 0;
78 mcb -> size = 0;
79
80 /*
81 * 已经进行了初始化
82 */
83 is_initialized = 1;
84 }
85
86 /*
87 * 模拟malloc函数
88 */
89 void * my_malloc(int size)
90 {
91 if (!is_initialized)
92 {
93 my_malloc_init();
94 }
95
96 /*分配的内存块的地址*/
97 void * mem_location = 0;
98
99 /*内存控制块的指针*/
100 mem_control_block *curr_mcb, *tail;
101
102 /*从头开始遍历*/
103 curr_mcb = memory_start;
104
105 while ((void*)curr_mcb < memory_end)
106 {
107 if (curr_mcb -> available)/*找到一个空闲块*/
108 {
109 if (curr_mcb -> size >= size && curr_mcb -> size < size + 2 * sizeof (mem_control_block))
110 /*大小合适,多余的空间不够进行分割的。也就是剩余的空间无法满足块头和块尾所需要的空间。*/
111 {
112 /*获得返回的内存地址*/
113 /*
114 * 此处必须把curr_mcb转成void*的格式!!
115 * 否则,在加的时候是以sizeof(mem_control_block)的倍数增加,而不是仅仅加一!!
116 */
117 mem_location = (void *) curr_mcb + sizeof (mem_control_block);
118 /*标记已经占用*/
119 curr_mcb -> available = 0;
120
121 /*获得边界标记的指针,块尾*/
122 tail = (void *) curr_mcb + sizeof (mem_control_block) + curr_mcb -> size;
123
124 /*标记已经占用*/
125 tail -> available = 0;
126 break;
127 }
128 else if (curr_mcb -> size > size + 2 * sizeof (mem_control_block))
129 /*进行分割*/
130 {
131 int old_size = curr_mcb -> size;
132 /*获得分配的内存块的地址*/
133 mem_location = (void *) curr_mcb + sizeof (mem_control_block);
134 /*标记已经占用*/
135 curr_mcb -> available = 0;
136 curr_mcb -> size = size;
137 /*获得边界标记的指针,块尾*/
138 tail = (void *) curr_mcb + sizeof (mem_control_block) + size;
139 /*标记已经占用*/
140 tail -> available = 0;
141 tail -> size = size;
142
143 /*将余下的部分分割成新的空闲块*/
144 mem_control_block *hd, *tl; /*新块的块头和块尾*/
145 /*块头*/
146 hd = (void *) tail + sizeof (mem_control_block);
147 hd -> available = 1;
148 hd -> size = old_size - size - 2 * sizeof (mem_control_block);
149 /*块尾*/
150 tl = (void *) hd + hd -> size + sizeof (mem_control_block);
151 tl -> available = 1;
152 tl -> size = hd -> size;
153
154 break;
155 }
156 }
157
158 /*指向下一个块*/
159 curr_mcb = (void*) curr_mcb + curr_mcb -> size + 2 * sizeof (mem_control_block);
160 }
161
162 /*没有找到合适的块,则扩展堆区,分配合适大小的内存加到堆区*/
163 if (!mem_location)
164 {
165 /*申请空间*/
166 if (sbrk(size + 2 * sizeof (mem_control_block)) <= 0)
167 {
168 printf("Sbrk Error!\n");
169 return 0;
170 }
171 /*设置控制块的信息*/
172 curr_mcb = (void*)memory_end - sizeof(mem_control_block);
173 curr_mcb -> available = 0;
174 curr_mcb -> size = size;
175 /*设置边界标记,块尾的信息*/
176 tail = (void*) curr_mcb + curr_mcb -> size + sizeof (mem_control_block);
177 tail -> available = 0;
178 tail -> size = size;
179
180 /*获得分配的内存块的地址*/
181 mem_location = (void*)curr_mcb + sizeof (mem_control_block);
182
183 memory_end = memory_end + size + 2 * sizeof (mem_control_block);
184 /*设置结尾块*/
185 tail = (void*)memory_end - sizeof(mem_control_block);
186 tail -> available = 0;
187 tail -> size = 0;
188 }
189
190 return mem_location;
191 }
192 /*
193 * 模拟free函数
194 */
195 void my_free(void *ptr)
196 {
197 if (ptr <= 0)
198 {
199 return;
200 }
201
202 mem_control_block *curr;
203 /*指向控制块的地址*/
204 curr = ptr - sizeof (mem_control_block);
205
206
207
208 /*标记为空闲,可用*/
209 curr->available = 1;
210 /*
211 * 合并
212 */
213 mem_control_block *pre, *next, *tmp;
214
215 /*获得前一个块的块头地址*/
216 pre = ptr - 2 * sizeof (mem_control_block);/*这条语句获得了前一个块的块尾的地址*/
217 pre = (void *) pre - pre -> size - sizeof (mem_control_block);/*进一步计算块头的地址*/
218
219 /*获得后一个块的块头地址*/
220 next = ptr + curr -> size + sizeof (mem_control_block);
221
222 if (!pre -> available && next -> available)/*只有后一个块空闲*/
223 {
224 curr -> size += (next -> size + 2 * sizeof (mem_control_block));
225 /*设置块尾*/
226 tmp = (void *) curr + curr -> size + sizeof (mem_control_block);
227 tmp -> available = 1;
228 tmp -> size = curr -> size;
229 }
230 else if (pre -> available && !next -> available)/*只有前一个块空闲*/
231 {
232 pre -> size += (curr -> size + 2 * sizeof (mem_control_block));
233 /*设置块尾*/
234 tmp = (void *) pre + pre -> size + sizeof (mem_control_block);
235 tmp -> available = 1;
236 tmp -> size = pre -> size;
237 }
238 else if (pre -> available && next -> available)/*前后都块空闲*/
239 {
240 pre -> size += (curr -> size + 4 * sizeof (mem_control_block) + next -> size);
241 /*设置块尾*/
242 tmp = (void *) pre + pre -> size + sizeof (mem_control_block);
243 tmp -> available = 1;
244 tmp -> size = pre -> size;
245 }
246
247 return;
248
249 }
250
251 void print_info()
252 {
253 printf("printf_info:\n");
254 mem_control_block *curr = memory_start;
255 while ((void*)curr < memory_end)
256 {
257
258 printf("a? %d s: %d %d %d\n", curr ->available, curr ->size, memory_end, curr);
259 curr = (void*) curr + curr -> size + 2 * sizeof (mem_control_block);
260 }
261 }
262
263
264 #ifdef __cplusplus
265 }
266 #endif
267
268 #endif /* _MY_ALLOC_H */
269
270