Kernighan& Ritchie malloc自由逻辑

时间:2014-05-05 23:18:57

标签: c malloc free implementation kernighan-and-ritchie

我在free()实现中花了几个小时处理一个特定条件。我搜索了网络和stackoverflow,看看是否有其他人讨论过这个,但我没有找到。我理解第187页及其中描述的3种功能背后的一般概念。第二版188(ANSI)。我是一名专业人士,大多数代码对我来说都很有意义,但是当我弄清楚所有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.

使用p == freep ==&amp; base仍然输入for循环。在此之后我在条件方面遇到麻烦。 for循环条件将首先执行 - 将基本(全局)地址(p == freep ==&amp; base still)与OS中新获得的空间的地址进行比较(注意 - 此地址通过free的参数得出)。在malloc中,我们将freep == p-&gt; s.ptr初始化为&amp; base。因此,此时p和p-> s.ptr都指向相同的地址。因此,for条件将为TRUE,因为bp不能同时为&gt;和&lt;相同的地址。注意否定,这使得循环条件为TRUE,我们输入if condition。

第5步:

if条件的第一部分是p> = p-> s.ptr。这是正确的,因为p == freep ==&amp; base == p-&gt; s.ptr(在malloc中设置)。 if条件的第二部分为TRUE,因为bp必须>或者&lt;相同的地址(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-&gt; s.ptr,这是&#39; base&#39;的地址。所以,执行else,并将新分配的标题设置为p-&gt; s.ptr == freep-&gt; s.ptr == freep ==&amp; base !!!为什么将这个新指针设置为&#39; base&#39;的地址?全球?

我的第二个问题是为什么2级间接(上面第3行代码)设置bp-&gt; 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-&gt; s.ptr == base.s.ptr == freep-&gt; s.ptr设置为新分配的内存。那么,在我们第一次通过malloc / morecore / free序列时,下一个语句freep = p是多余的?在所有这些步骤结束时,freep仍然指向&#39; base&#39; ?没有变化 ?感觉就像我在逻辑上犯了一些错误。

关于ANSI标准C关于工会的说法的一点澄清。这可以追溯到我上面使用&#39; typedef&#39;的第一个代码snipet。我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零?如果我没有将它们声明为静态怎么办?在这种情况下struct s.header ptr会为NULL吗?

如果我在第7步的逻辑中犯了任何错误,请指出第一遍。我还要感谢,如果有人花时间写清楚(我已经完成的方式)free()如何工作以及free()中各种条件的伪代码。这个帖子对学生/新手非常有用。

感谢您的时间和解释。

1 个答案:

答案 0 :(得分:1)

  

为什么要将这个新指针设置为&#39; base&#39;的地址?全球?

在malloc实现的描述中,它说最后一个块有指向第一个块的指针。因为第一次调用函数你不会有任何块,所以这个块指向自身。

  

那么,在我们第一次执行malloc / morecore / free序列时,下一个语句freep = p是多余的?在所有这些步骤结束时,freep仍然指向&#39; base&#39; ?没有变化 ?感觉就像我在逻辑上犯了一些错误。

我相信,在第一次调用malloc时,你的推理中没有错误 freep = p = &base 。 我想,通常这段代码非常棘手。在这个问题(Explain this implementation of malloc from the K&R book)中,您可以找到更多关于k&amp; r malloc()实现的批评

但是对于你的情况,我认为名字free()让你感到困惑。因为函数free()实际上有两个目的:初始化全新的自由列表并显然释放以前分配的内存。另外&#34;代码完成&#34; S.Macconnell说,具有多种用途的功能并不是一个好主意。

所以在第一次通过中,实际上你不需要一些额外的任务(初始化free()的制度)。但是在free的下一次调用(实际释放先前分配的内存的状态)对于指向最后一块空闲列表内存以便合并内存非常有用。

  

关于ANSI标准C关于工会的说法的一点澄清。这可以追溯到我上面使用&#39; typedef&#39;的第一个代码snipet。我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零?

是的,引自c89 draft(3.5.7。初始化):

  

如果未初始化具有静态存储持续时间的对象   显式地,它被隐式初始化,好像每个成员都有   算术类型被赋值为0,每个成员都有指针类型   被赋予空指针常量。如果一个对象有自动   存储持续时间未明确初始化,其值为   不确定的。


  

如果我没有将它们声明为静态怎么办?在这种情况下struct s.header ptr会为NULL吗?

我相信这将是垃圾。