繁体   English   中英

Kernighan&Ritchie malloc自由逻辑

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM