C语言实现队列review考虑到的知识点

在stackexchange有一个人提了这么一个问题,自己实现了一个通用队列,然后把代码贴了上来,然后请大家review以下,希望基于以下几方面提一些建议:

  • 1,编程风格(My general style)
  • 2,是否正确地释放内存以避免内存泄漏
  • 3,正确地处理了内存分配失败
  • 4,其它任何你能想得到的建议

在我看来,下面的实现其实已经相当完备了,代码正确性,风格,可读性都相当不错,可是下面还有一人竟然提出了12条改进建议,让我觉得相当惊讶,我看了下这个人的简介,coding since 1976,让我觉得一个有着多年经验的程序员和普通程序员之间的差别
代码如下:

Queue.h
#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED

typedef struct Node
{
  void *data;
  struct Node *next;
}node;

typedef struct QueueList
{
    int sizeOfQueue;
    size_t memSize;
    node *head;
    node *tail;
}Queue;

void queueInit(Queue *q, size_t memSize);
int enqueue(Queue *, const void *);
void dequeue(Queue *, void *);
void queuePeek(Queue *, void *);
void clearQueue(Queue *);
int getQueueSize(Queue *);

#endif /* QUEUE_H_INCLUDED */
Queue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Queue.h"

void queueInit(Queue *q, size_t memSize)
{
   q->sizeOfQueue = 0;
   q->memSize = memSize;
   q->head = q->tail = NULL;
}

int enqueue(Queue *q, const void *data)
{
    node *newNode = (node *)malloc(sizeof(node));

    if(newNode == NULL)
    {
        return -1;
    }

    newNode->data = malloc(q->memSize);

    if(newNode->data == NULL)
    {
        free(newNode);
        return -1;
    }

    newNode->next = NULL;

    memcpy(newNode->data, data, q->memSize);

    if(q->sizeOfQueue == 0)
    {
        q->head = q->tail = newNode;
    }
    else
    {
        q->tail->next = newNode;
        q->tail = newNode;
    }

    q->sizeOfQueue++;
    return 0;
}

void dequeue(Queue *q, void *data)
{
    if(q->sizeOfQueue > 0)
    {
        node *temp = q->head;
        memcpy(data, temp->data, q->memSize);

        if(q->sizeOfQueue > 1)
        {
            q->head = q->head->next;
        }
        else
        {
            q->head = NULL;
            q->tail = NULL;
        }

        q->sizeOfQueue--;
        free(temp->data);
        free(temp);
    }
}

void queuePeek(Queue *q, void *data)
{
    if(q->sizeOfQueue > 0)
    {
       node *temp = q->head;
       memcpy(data, temp->data, q->memSize);
    }
}

void clearQueue(Queue *q)
{
  node *temp;

  while(q->sizeOfQueue > 0)
  {
      temp = q->head;
      q->head = temp->next;
      free(temp->data);
      free(temp);
      q->sizeOfQueue--;
  }

  q->head = q->tail = NULL;
}

int getQueueSize(Queue *q)
{
    return q->sizeOfQueue;
}
TestQueue.c
#include <stdio.h>
#include <stdlib.h>
#include "Queue.h"

int main()
{
    int val;
    Queue q;

    queueInit(&q, sizeof(int));

    for(val = 0; val < 10; val++)
    {
        enqueue(&q, &val);
        printf("The value %d has been enqueued.\n", val + 1);
    }

    printf("\n");

    queuePeek(&q, &val);

    printf("The value that is at the front of the queue is %d\n\n", val + 1);

    while(getQueueSize(&q) > 0)
    {
        dequeue(&q, &val);
        printf("%d has been dequeued.\n", val + 1);
    }

    return 0;
}

下面让我看看他提的一些建议:

  • 1,Pattern-less function names。对于queue.h里面一些函数的命名, Queue_Init(), Queue_GetSize() 要比 queueInit(), getQueueSize() 更能凸显意义,更具有一致性。
  • 2,Comments with # preprocessing may not be portable。注释带了#可能移植性不太好。这个没理解,原程序中似乎也没有这样用注释,哪位读者明白可以评论里面补充下
// #endif /* QUEUE_H_INCLUDED */
#endif
  • 3,QueueList没有必要保存两个head和tail两个指针,只需要保存tail指针,并且tail结点的next指针域指向头结点,这实际上是一个循环链表组成的queue,那么链表走到末端的判断将会是p->next == tail->next。这样的好处是大量使用queue的时候更节省内存,但是逻辑上复杂了些。
  • 4 不够明晰为什么队列大小类型使用int表示。int是(有符号)signed类型,可能表示负数,也许可以使用unsigned,在大多数系统中,size_t表示的范围要比int更加宽广,也许使用size_t更加谨慎一些。健壮的代码应该在enqueue函数中检查进队时是否超过队列最大长度。
  • 5 好的点:声明memSize为size_t类型,对于malloc()函数的错误检查,有测试用例。另外对于.h的使用有注释说明,对于你的代码来说.h就像是一个公共借口。

关于size_t,int和unsigned int可参考此文https://prateekvjoshi.com/2015/01/03/why-do-we-need-size_t/, size_t,size_t的取值range是目标平台下最大可能的数组尺寸(非负数),一些平台下size_t的范围小于int的正数范围,又或者大于unsigned int。为了移植性和性能考虑,有些情况下表示大小尽可能使用size_t

  • 6 更加健壮的用法在queueInit()函数中检查memSize==0,同样对于malloc()函数应该做以下检查:if(newNode->data == NULL && q->memSize > 0)
  • 7 要有些文档化的解释。对于queue.h每个函数声明前面建议由一到两行的注释。
  • 8 对于queuePeek(Queue *q, void data)函数,如果q不改变,则应该声明为queuePeek(const Queue *q, void *data)。这是对于q常量特性的自我文档说明,可能对编译器优化也有好处。另外对于函数的使用实现检查也是有好处。
  • 9 为了完备性,建议在clearQueue()函数中写上q->memSize = 0 。
  • 10 改变#include的包含顺序。把"queue.h"应该放在第一个。这样可以检测到queue.h没有依赖下面的三个<.h>文件。queue.h需要依赖的已经在其本身文件中包含进来。
#include "Queue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include "Queue.h"

关于头文件依赖顺序说明可以参看:https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers/
1,foo/bar.h.
2,C 系统文件
3,C++系统文件
4,其他库头文件
5,本项目内头文件,
实际上《C++编程思想》对于2,3,4,5建议的顺序可能是反过来的,两者各有利弊。但有一点本文件相关的头文件必须放在第一位,可以防止在本文件(如上面的bar.h)里面少包含了必须的头文件。

  • 11 结果声明。对于void dequeue(Queue *q, void *data)函数中拷贝data的操作,没有对结果的声明,应该失败返回fasle,成功返回true。queuePeek()函数也类似。
  • 12 为了调试方便,释放内存操作free()之前最好把内存数据置零,这样错误的代码可能在面对原始数据对比为0的指针或者数据更快失败,更容易调试。
  • 13 建议:如果非必要,可以不用存储queue的size大小,在需要的时候计算即可。也许可以添加一个检查是否为空队列的函数bool Queue_IsEmpty(const Queue *q)。这样可以去掉size的声明。

另外还有一个回答建议Node结构体的声明可以在.c文件中定义,如果其不是对外公开的数据结构体。

原文网址:

https://codereview.stackexchange.com/questions/141238/implementing-a-generic-queue-in-c

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,665评论 0 3
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,674评论 0 38
  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,622评论 0 3
  • 1、早上团队代码走查,庄同学在代码走查和自己的代码中都用到了我之前分享的JDK库函数来简化代码,说明之前的分享还有...
    玉露君阅读 254评论 0 0
  • 花铺了一路芳艳,风里没有一丝跌落枝头的惆怅。只有淡淡幽香,融在温温暖暖的阳光里,像梦里人儿的纤柔手指,想轻吻却怕不...
    凡夫555阅读 205评论 4 7