[英]Kernighan & Ritchie malloc free logic
我在free()实现中花了几个小时处理一个特定的条件。 我搜索了网络和stackoverflow,看看是否有其他人讨论过这个,但我没有找到。 我理解第二版(ANSI)第187页和第188页中描述的3个功能背后的一般概念。 我是一名专业人士,大部分代码对我来说都很有意义,但是当我弄清楚所有3个函数如何在第一次调用时工作时(换句话说,将* freep初始化为某些东西),我感到难过。
以下是我从代码中得出的步骤:步骤1.有两个全局变量定义如下
typedef long Align;
union header { /* block header */
struct {
union header *ptr; /* next block if on free list */
unsigned size; /* size of this block */
} s;
Align x; /* force alignment of blocks */
};
typedef union header Header;
static Header base; /* empty list to get started */
static Header *freep = NULL; /* start of free list */
第2步:首先通过malloc()函数,因为第一次,当用户调用malloc()时,上面给出的2个全局变量将不包含任何可用数据。 我在本节下面的书中附上了malloc()的代码。 我理解'nunits'是如何计算的。 如果freep == NULL,则将freep&prevp指向&base。 因此,首次输入for循环,其中p == prevp-> s.ptr == base.s.ptr == freep == prevp ==&base(sic)。 下一个if条件为false,因为base.s.size在第一次调用期间已设置为零。 现在,下一个if条件(p == freep)为真,因为它们都指向'base'的地址。 因此,此时我们调用morecore(),这将在下面的步骤3中描述。
/* malloc: general-purpose storage allocator */
void *malloc(unsigned nbytes)
{
Header *p, *prevp;
Header *morecore(unsigned);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
if ((prevp = freep) == NULL) { /* no free list yet */
base.s.ptr = freeptr = prevptr = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) /* exactly */
prevp->s.ptr = p->s.ptr;
else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits
}
freep = prevp;
return (void *)(p+1);
}
if (p == freep) /* wrapped around free list */
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left */
} // end of for loop
}
第3步:morecore()
到目前为止,所有指针都指向&base(全局),我们输入morecore()调用OS以获得可用内存空间。 morecore()然后将此内存的开头初始化为Header结构,如下所示:
Header *up;
up = the memory from OS
up->s.size = nu; (number of units, each of size = sizeof(Header))
free( (void*) (up+1) );
return freep;
在init之后,morecore()使用指向下一个地址的参数(在初始Header结构之后)调用free()。 morecore()不会改变freep global,它仍然指向&base。
第4步:输入free()。
我的问题是自由功能,所以我只给出下面的相关行。
void free( void* ap)
{
Header *bp, *p; // a pointer to base (ie. Header) and a temporary pointer p.
bp = (Header *)ap - 1; // point to block header
for (p = freep; !(bp > p && bp < p->s.ptr) ; p = p->s.ptr)
if ( p >= p->s.ptr && (bp > p || bp < p->s.ptr) )
break; // freed block at start or end of arena.
输入for循环时,p == freep ==&base still。 在此之后我在条件方面遇到麻烦。 将首先执行for循环条件 - 将基本(全局)地址(p == freep ==&base still)与来自OS的新获取空间的地址进行比较(注意 - 此地址通过free的参数来进行)。 在malloc中,我们将freep == p-> s.ptr初始化为&base。 因此,此时p和p-> s.ptr都指向相同的地址。 因此,for条件将为TRUE,因为bp不能同时为>和<相同的地址。 注意否定,这使得循环条件为TRUE,如果条件,我们输入。
第5步:
if条件的第一部分是p> = p-> s.ptr。 这是正确的,因为p == freep ==&base == p-> s.ptr(在malloc中设置)。 if条件的第二部分为TRUE,因为bp必须是>或<相同的地址(p == p-> s.ptr)。 因此,我们打破for循环因为释放的块是竞技场的开始(或竞技场的结束,如果我们通过malloc维护的循环链接的免费记忆列表达到这一点)。
第6步:我的问题在这里
if(bp + bp->s.size == p->s.ptr) { // join to upper nbr
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
}
else bp->s.ptr = p->s.ptr;
继续... for循环之后的第一个if条件为假,因为新分配的内存空间不等于p-> s.ptr,这是'base'的地址。 所以,执行else,并将新分配的头设置为p-> s.ptr == freep-> s.ptr == freep ==&base !!! 为什么这个新指针被设置为'base'全局地址?
我的第二个问题是为什么2级间接(上面第3行代码)设置bp-> s.ptr,以防条件满足? (我知道这是加入连续的块。)
STEP 7:加入较低的nbr(是书中的评论)
if (p + p->s.size == bp) {
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
}
else p->s.ptr = bp;
freep = p;
由于同样的原因,在第一次传递期间,下一个if条件也将为false,而else子句将p-> s.ptr == base.s.ptr == freep-> s.ptr设置为新分配的内存。 那么,在我们第一次通过malloc / morecore / free序列时,下一个语句freep = p是多余的? 在所有这些步骤结束时,freep仍然指向'base'的地址? 没有变化 ? 感觉像我在逻辑上犯了一些错误。
关于ANSI标准C关于工会的说法的一点澄清。 这可以追溯到上面第一个带有'typedef'的代码snipet。 我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零? 如果我没有将它们声明为静态怎么办? 在这种情况下struct s.header ptr是否为NULL?
如果我在第7步的逻辑中犯了任何错误,请指出第一遍。 我还要感谢,如果有人花时间写清楚(我已经完成的方式)free()如何工作以及free()中各种条件的伪代码。 这个线程对学生/新手非常有用。
感谢您的时间和解释。
为什么这个新指针被设置为'base'全局地址?
在malloc实现的描述中,据说最后一个块具有指向第一个块的指针。 并且由于第一次调用函数,你没有任何块,因此这个块指向自身。
那么,在我们第一次通过malloc / morecore / free序列时,下一个语句freep = p是多余的? 在所有这些步骤结束时,freep仍然指向'base'的地址? 没有变化 ? 感觉像我在逻辑上犯了一些错误。
我相信,在第一次调用malloc时,你的推理中没有关于freep = p = &base
错误。 我想,通常这段代码非常棘手。 在这个问题中( 从K&R书中解释malloc的这种实现 ),您可以找到更多关于k&r malloc()
实现的批评
但对于你的情况,我认为名字free()
让你感到困惑。 因为函数free()
实际上有两个目的:初始化全新的自由列表并显然释放以前分配的内存。 S.Macconnell的“代码完整”说,具有多种用途的功能并不是一个好主意。
所以在第一遍中,实际上你不需要一些额外的任务(初始化free()
制度)。 但是在下一次free
调用(实际释放先前分配的内存的方式)对于指向最后一块空闲列表内存以便合并内存很有用。
关于ANSI标准C关于工会的说法的一点澄清。 这可以追溯到上面第一个带有'typedef'的代码snipet。 我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零?
是的,引用c89草案 (3.5.7。初始化):
如果没有显式初始化具有静态存储持续时间的对象,则会隐式初始化它,就好像每个具有算术类型的成员都被赋值为0,并且每个具有指针类型的成员都被赋予空指针常量。 如果未显式初始化具有自动存储持续时间的对象,则其值不确定。
如果我没有将它们声明为静态怎么办? 在这种情况下struct s.header ptr是否为NULL?
我相信这将是垃圾。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.