CSAPP Lab0 队列

实验0 课程先导实验报告

一. 代码实现部分

1.1 队列节点与队列数据结构

  • 修改后代码的数据结构

    typedef struct ELE
    {
      char *value;
      struct ELE *next;
    } list_ele_t;
    
    /* 队列结构 */
    typedef struct
    {
      list_ele_t *head;   /* 头指针 */      
      list_ele_t *tail;   /* 尾指针 */
      int size;           /* 节点数量 */
    } queue_t;
    

    ​ list_ele_t 结构和 queue_t 结构一起实现如下图所示的字符串队列。queue_t 结构定义中有一个域 head,存储指向队列的指针。而另一个域tail,则储存队列的尾部的节点,最后添加int型数据size来记录添加节点个数,每个元素用 list_ele_t 结构表示,有 value 和 next 两个域,存储指向一个字符串的指针和指向下一个链表元素的指针,字符用ASCII编码,最后一个元素的next 指针设置为 NULL。

  • 数据结构参考图

在这里插入图片描述

1.2 q_new的实现

  • 完成后代码

    queue_t *q_new()
    {
      queue_t *q = malloc(sizeof(queue_t));
       
      if (q == NULL)	// 若空则返回NULL
        return NULL;
    
      q->tail = NULL;
      q->head = NULL;   // 初始化各个指针
      q->size = 0;		// 初始化size
      return q;
    }
    

    ​ 该部分修改对malloc返回空进行了处理,同时也对新添加的域进行初始化。

1.3 q_free的实现

  • 完成后代码

    void q_free(queue_t *q)
    {
      if (q == NULL)
        return;
    
      list_ele_t *temp;
    
      while (q->head != NULL)
      {
        temp = q->head;
        q->head = q->head->next;
          
        free(temp->value);
        temp->value = NULL;
          
        free(temp);
        temp = NULL;
      }
      free(q);
    }
    

    ​ 该部分实现思路为从头节点下手,用temp指针充当临时指针来释放头节点,并且使用queue结构的头指针指向其下一个节点以防节点丢失。

  • 思路参考图

在这里插入图片描述

1.4 q_insert_head的实现

  • 完成后代码

    bool q_insert_head(queue_t *q, char *s)
    {
      if (q == NULL)
        return false;
    
      list_ele_t *newh;
      newh = malloc(sizeof(list_ele_t));
      if (newh == NULL)
        return false;
    
      newh->value = malloc(strlen(s) + 1);
      if (newh->value == NULL)
      {
        free(newh);
        return false;
      }
    
      strcpy(newh->value, s);
      newh->next = NULL;
    
      if (q->head == NULL)
        q->tail = newh;
    
      newh->next = q->head;
      q->head = newh;
      q->size++;
    
      return true;
    }
    

    ​ 该部分代码修改后考虑到了malloc失败的情况以及第一次插入的情况。malloc失败的情况分两个部分,第一种情况是newh分配失败,那么就可以直接返回false而不必考虑后续情况,第二种情况为newh->value分配失败,这种情况下返回false前需要释放之前成功分配的newh的空间以免发生内存泄漏。第一次插入需要考虑到尾指针的情况,此时因为第一次插入,所以此时的节点即是第一个也是最后一个,因此需要将尾指针指向该节点。当然,每次节点插入成功我们都会使size++用来记录此时节点数量。

  • 思路参考图

在这里插入图片描述

  • 第一次插入参考图(由于与尾插法思虑相似,故尾插法不再展示第一次插入)

在这里插入图片描述

1.5 q_insert_tail的实现

  • 完成后代码

    bool q_insert_tail(queue_t *q, char *s)
    {
      if (q == NULL)
        return false;
    
      list_ele_t *newh;
      newh = malloc(sizeof(list_ele_t));
      if (newh == NULL)
        return false;
    
      newh->value = malloc(strlen(s) + 1);
      if (newh->value == NULL)
      {
        free(newh);
        return false;
      }
    
      strcpy(newh->value, s);
      newh->next = NULL; 
    
      if (q->tail == NULL)
      {
        q->head = newh;
        q->tail = newh;
      }
    
      q->tail->next = newh;
      q->tail = newh;
      q->size++;
    
      return true;
    }
    

    ​ 该部分代码整体与头插法相似,故具体思路可参考 1.4 部分头插法代码说明。

  • 思路参考图

在这里插入图片描述

1.6 q_remove_head的实现

  • 完成后代码

    bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
    {
      if (q == NULL || q->head == NULL)
        return false;
    
      if (sp != NULL)
      {
        strncpy(sp, q->head->value, bufsize - 1);
        sp[bufsize - 1] = '\0';
      }
    
      list_ele_t *temp = q->head;
      q->head = q->head->next;
    
      free(temp->value);
      temp->value = NULL;
        
      free(temp);
      temp = NULL;
        
      q->size--;
    
      return true;
    }
    

    ​ 该部分代码主要为字符串拷贝部分以及释放内存空间部分。字符串拷贝需要遵守bufsize的大小,否则调试代码的时候会发生溢出,并且’\0’的置入也非常重要,否则显示的sp后面会出现许多 X 。释放空间部分需要注意先释放value后释放节点,否则会丢失指针。

  • 思路参考图

    该思路参考图可参考 1.3 部分的参考图,去掉第四步即是我们需要的参考图。

1.7 q_size的实现

  • 完成后代码

    int q_size(queue_t *q)
    {
      if (q != NULL && q->head != NULL)
        return q->size;
      return 0;
    }
    

    ​ 该部分代码只需注意空队列返回0即可。而size的大小只需访问结构体中记录的size就可以知道队列的大小。

1.8 q_reverse的实现

  • 完成后代码

    void q_reverse(queue_t *q)
    {
      if (q == NULL || q->head == NULL)
        return;
    
      list_ele_t *temp1;
      list_ele_t *temp2;
    
      temp1 = q->head;
      q->tail->next = q->head;
      q->head = q->head->next;
      temp1->next = NULL;
    
      while (q->head != q->tail)
      {
        temp2 = q->head;
        q->head = q->head->next;
        temp2->next = q->tail->next;
        q->tail->next = temp2;
      }
    
      q->tail = temp1;
      temp1 = temp2 = NULL;
    }
    

    ​ 该部分代码思路主要分为两个部分:第一个部分主要记录旧头节点的位置并将其移动至尾部,然后将新头指针指向下一个节点;第二部分主要将不断产生的新头节点移至旧尾节点的前面,不断循环至头指针指向旧尾节点,最后将尾指针指向第一部分记录的旧头节点。

  • 思路参考图第一部分

在这里插入图片描述

  • 思路参考图第二部分

在这里插入图片描述

二. 代码测试部分

​ 代码测试截图

在这里插入图片描述

通过不断调试与修改得到以上结果

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值