第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三)

     “啊……”小P舒服的伸着懒腰,“你知道什么是九三学社么?”
     “?”
     “就是上午睡到9点,下午睡到3点……舒服……”小P用手拍拍嘴,打了一个哈欠。
     “呵呵,睡觉睡到自然醒,真是我们学生一大福利啊……”老C感叹,“好了,闲话少说,你去开机……我去接水……”
     “哦……”
     两人折腾一番后,终于坐在小P桌前。
     “我们现在根据程序的模块将它分成几部分,我来写,你看看。”老C开始在电脑键盘上扣扣扣扣的敲打起来,“我们的程序大体上分为三个主要部分,一个是 main()函数,这个是程序的进入点和主框架结构,负责算法部分;一个是我们的MY_DEBUG调试宏;再一个是我们的apple game游戏,提供接口供算法部分调用。根据这样的划分,每一个部分的规模和所担负的责任就很清楚了。”老C一边说,一边在工程中添加了新的文件,并改写 了main.c文件的内容。

mydebug.h:

#if !defined(MY_DEBUG_H_)
#define MY_DEBUG_H_

#include <stdio.h>

#define PRINT_DEBUG_INFO


#if defined(PRINT_DEBUG_INFO)
#define MY_DEBUG(str)            printf(str)
#define MY_DEBUG_1(str, par)    printf(str, par)
#else
#define MY_DEBUG(str)
#define MY_DEBUG_1(str, par)
#endif /* PRINT_DEBUG_INFO */


#endif /* MY_DEBUG_H_ */

------------------------------------------------------(华丽的分割线)

applegame.h:

#if !defined(APPLE_GAME_H_)
#define APPLE_GAME_H_

#define CHILDREN_NUM    20U

typedef int SEAT_NUM;
typedef enum tagEXISTE_STATE { ABSENT, EXISTED } EXISTE_STATE;
typedef struct tagCHILD
{
    SEAT_NUM        seatNum_;
    EXISTE_STATE    existeState_;
}CHILD;

#define QUEUE_LENGTH    CHILDREN_NUM
typedef CHILD QUEUE_CONTENT;
typedef struct tagQUEUE
{
    int size_;
    int index_;
    QUEUE_CONTENT queue_[QUEUE_LENGTH];
}QUEUE;

typedef struct tagAPPLE_GAME
{
    int currCountNum_;
    int childrenRemained_;
    QUEUE childrenQueue_;
}APPLE_GAME;

extern void InitAppleGame (APPLE_GAME* game);
extern int IsGameOver (APPLE_GAME* game);
extern void PlayGame (APPLE_GAME*  game);
extern int LastChildSeatNum (APPLE_GAME* game);

#endif /* APPLE_GAME_H_ */

------------------------------------------------------(华丽的分割线)

applegame.c:

#include "applegame.h"
#include "mydebug.h"

void InitAppleGame(APPLE_GAME* game)
{
    MY_DEBUG("Init the apple game.\n");
}

int IsGameOver(APPLE_GAME* game)
{
    static int n = -1;

    MY_DEBUG("Only one child?\n");
    ++n;

    return n;
}

void PlayGame(APPLE_GAME* game)
{
    MY_DEBUG("Play game...\n");
}

int LastChildSeatNum(APPLE_GAME* game)
{
    int n = 1;
    MY_DEBUG("Searching last child's seat number\n");

    return n;
}


------------------------------------------------------(华丽的分割线)

main.c:

#include <stdio.h>
#include "applegame.h"

int main()
{
    APPLE_GAME theGame;
    int num;
    
    /* Initialize the game. */
    InitAppleGame(&theGame);

    /* Play the game, until the last child is found. */
    while (!IsGameOver(&theGame))
    {
        PlayGame(&theGame);
    }

    /* Search the last child's seat number.  */
    num = LastChildSeatNum(&theGame);

    printf("The last child's seat number is %d.\n", num);

    return 0;
}

     “编译……运行……OK,我们的V0.03版本成功了……”老C道,他依照以前的办法将各个新文件拷贝到AppleGame_V0.03目录下,又新建了AppleGame_V0.04。 “我们的原则是尽量少的包含依赖关系。每个文件都包含且仅包含它们需要的头文件,既不能多,也不能少。比如main.c,就算我知道 applegame.h中包含了stdio.h,但是并没有明显的线索和强烈的暗示告诉我applegame.h中一定包含stdio.h,所以我还是要 包含stdio.h,这样就减小了main.c对applegame.h的一些依赖……如果哪天我一高兴,决定不再在applegame.h中包含 stdio.h,这样就可以少修改一些地方……偷懒,是程序员的美德……”
     “嗯……”无视了老C的自吹自擂,小P看了几遍工程中的文件,“嗯,这样的确比写在一起清晰很多。然后呢?”
     “然后我们就继续细化我们的代码,你来接着写啊。注意编码的规范性……”老C答道。
     “好!”小P开始挽袖子。
     “不过在此之前我先教你一个乖。”老C将applegame.c文件打开,“你按一下Ctrl+Tab试试。”
     “唔……哦?文本跳转到了applegame.h?这个……好东东啊。”小P试了试。
     “呵呵,你可以修改这个快捷键,我记得好像叫toggle head & source,因为某些恶趣味我改成了Alt+O,哈哈……”
     “囧……”忽略过老C的傻笑,小P开始完善applegame.c文件。

     经过一阵忙碌,小P修改了applegame.h和applegame.c文件。

applegame.h:

#if !defined(APPLE_GAME_H_)
#define APPLE_GAME_H_

#define CHILDREN_NUM    20U
#define KICK_OUT_NUM    7U

typedef int SEAT_NUM;
typedef enum tagEXISTE_STATE { ABSENT, EXISTED } EXISTE_STATE;
typedef struct tagCHILD
{
    SEAT_NUM        seatNum_;
    EXISTE_STATE    existeState_;
}CHILD;

#define QUEUE_LENGTH    CHILDREN_NUM
typedef CHILD QUEUE_CONTENT;
typedef struct tagQUEUE
{
    int size_;
    int index_;
    QUEUE_CONTENT queue_[QUEUE_LENGTH];
}QUEUE;

typedef struct tagAPPLE_GAME
{
    int currCountNum_;
    int kickOutNum_;
    int childrenRemained_;
    QUEUE childrenQueue_;
}APPLE_GAME;

extern void InitAppleGame (APPLE_GAME* game);
extern int IsGameOver (APPLE_GAME* game);
extern void PlayGame (APPLE_GAME*  game);
extern int LastChildSeatNum (APPLE_GAME* game);

#endif /* APPLE_GAME_H_ */

------------------------------------------------------(华丽的分割线)

applegame.c:

#include "applegame.h"
#include "mydebug.h"

void InitAppleGame(APPLE_GAME* game)
{
    int i;
    
    MY_DEBUG("Init the apple game.\n");

    game->currCountNum_      = 0;
    game->kickOutNum_        = KICK_OUT_NUM;
    game->childrenRemained_  = CHILDREN_NUM;

    game->childrenQueue_.size_  = CHILDREN_NUM;
    game->childrenQueue_.index_ = 0;
    for (i = 0; i < game->childrenQueue_.size_; ++i)
    {
        game->childrenQueue_.queue_[i].seatNum_ = i + 1;
        game->childrenQueue_.queue_[i].existeState_ = EXISTED;
    }
}

int IsGameOver(APPLE_GAME* game)
{
    MY_DEBUG_1("The children remained %d\n", game->childrenRemained_);

    return (1 == game->childrenRemained_);
}

void PlayGame(APPLE_GAME* game)
{
    MY_DEBUG("Play game...\n");

    /* If the current child is existed in the queue, count on, then check if she will be kicked out. */
    if (EXISTED == game->childrenQueue_.queue_[game->childrenQueue_.index_].existeState_)
    {
        /* Count on. */
        game->currCountNum_++;

        /* If the child counts kicked out number, then she is kicked out. */
        if (game->currCountNum_ == game->kickOutNum_)
        {
            game->childrenQueue_.queue_[game->childrenQueue_.index_].existeState_ = ABSENT;
            game->childrenRemained_--;
            game->currCountNum_ = 0;

            MY_DEBUG_1("The child kicked out is %d\n", game->childrenQueue_.queue_[game->childrenQueue_.index_].seatNum_);
        }
    }
    
    game->childrenQueue_.index_++;
    game->childrenQueue_.index_ %= game->childrenQueue_.size_;
}

int LastChildSeatNum(APPLE_GAME* game)
{
    int i;

    MY_DEBUG("Searching last child's seat number\n");

    for (i = 0; i < game->childrenQueue_.size_; ++i)
    {
        if (EXISTED == game->childrenQueue_.queue_[i].existeState_)
        {
            break;
        }
    }

    return game->childrenQueue_.queue_[i].seatNum_;        
}

   “呵呵,代码写得还可以,但是程序执行结果到底对不对呢?”老C看着代码问道,“你可以在applegame.h中将CHILDREN_NUM的值改为1U试试看边界的情况,然后将CHILDREN_NUM的值改为3U,KICK_OUT_NUM的值改为2U,试试看调试输出的内容是否正确。”
     “好啊。”小P试着更改了几下,看了看调试输出内容和自己在纸上画出的内容,“好像没有什么不对,我又用5个小朋友和数到3被提出试了试,结果也是正确的。”
     “OK,这么说我们的代码经过你的测试是正确的啦,我们终于有了自己的第一个正式版本啦。”说完老C将所有文件拷贝到AppleGame_V0.04,然后又将AppleGame_V0.04重新命名为AppleGame_
V1.00,“呵呵,这下你可以对比一下我们的V1.00版本和你原来的版本。”
     “唔,现在的版本代码变得多得多,但是……看起来更容易明白,而且比较容易调试和测试。”小P分析道。
     “没错!代码显得多是因为增加了函数,因为问题的规模很小,所有我们这样做有些麻烦——但这是练习——等我们熟悉了正规的做法再去进行直截了当的做法吧, 这样基本功更扎实一些。”老C点头,“我们再来优化一下applegame.c文件吧,因为里面的结构体点点点的看着实在是烦人。”他又建立了 AppleGame_V1.01目录。
     “唔,看着的确挺烦人的,那么你打算怎么优化么?”小P问。
     “呵呵,把烦人的操作放到函数后面,这样程序的结构和意图会更加明显。”老C回答。
     “怎么做呢?”
     “呵呵,您瞧好咧……”老C又开始敲键盘,优化applegame.c文件。

applegame.c:

#include "applegame.h"
#include "mydebug.h"

static void QueInitQueue (QUEUE* childQueue);
static int QueIsChildExisted (QUEUE* childQueue);
static void QueKickOutChild (QUEUE* childQueue);
static void QueMoveToNextChild (QUEUE* childQueue);
static int QueFindRemainedChild (QUEUE* childQueue);

void InitAppleGame(APPLE_GAME* game)
{
    MY_DEBUG("Init the apple game.\n");

    game->currCountNum_        = 0;
    game->kickOutNum_        = KICK_OUT_NUM;
    game->childrenRemained_    = CHILDREN_NUM;

    QueInitQueue(&(game->childrenQueue_));
}


int IsGameOver(APPLE_GAME* game)
{
    MY_DEBUG_1("The children remained %d\n", game->childrenRemained_);

    return (1 == game->childrenRemained_);
}

void PlayGame(APPLE_GAME* game)
{
    MY_DEBUG("Play game...\n");

    /* If the current child is existed in the queue, count on, then check if she will be kicked out. */
    if (QueIsChildExisted(&(game->childrenQueue_)))
    {
        /* Count on. */
        game->currCountNum_++;

        /* If the child counts kicked out number, then she is kicked out. */
        if (game->currCountNum_ == game->kickOutNum_)
        {
            QueKickOutChild(&(game->childrenQueue_));
            game->childrenRemained_--;
            game->currCountNum_ = 0;

            MY_DEBUG_1("The child kicked out is %d\n", game->childrenQueue_.queue_[game->childrenQueue_.index_].seatNum_);
        }
    }
    
    QueMoveToNextChild(&(game->childrenQueue_));
}

int LastChildSeatNum(APPLE_GAME* game)
{
    int seatNum;

    MY_DEBUG("Searching last child's seat number\n");

    seatNum = QueFindRemainedChild(&(game->childrenQueue_));

    return seatNum;
}


/************************************************************************/
/* Local functions                                                      */
/************************************************************************/
static void QueInitQueue(QUEUE* childQueue)
{
    int i;

    childQueue->size_    = CHILDREN_NUM;
    childQueue->index_ = 0;
    for (i = 0; i < childQueue->size_; ++i)
    {
        childQueue->queue_[i].seatNum_ = i + 1;
        childQueue->queue_[i].existeState_ = EXISTED;
    }
}

static int QueIsChildExisted(QUEUE* childQueue)
{
    return (EXISTED == childQueue->queue_[childQueue->index_].existeState_);
}

static void QueKickOutChild(QUEUE* gameQueue)
{
    gameQueue->queue_[gameQueue->index_].existeState_ = ABSENT;
}

static void QueMoveToNextChild(QUEUE* gameQueue)
{
    gameQueue->index_++;
    gameQueue->index_ %= gameQueue->size_;
}

static int QueFindRemainedChild(QUEUE* gameQueue)
{
    int i;
    
    for (i = 0; i < gameQueue->size_; ++i)
    {
        if (EXISTED == gameQueue->queue_[i].existeState_)
        {
            break;
        }
    }

    return gameQueue->queue_[i].seatNum_;    
}

     “编译……运行……ok,我们的V1.01版本也好了。”老C又将所有文件拷贝到AppleGame_V1.01目录下。
     “等等,”小P问道,“我看不出有什么实质性的变化啊,无非就是用一些static函数替换了原来的内容,换汤不换药啊。”
     “呵呵,你看不出区别是因为你熟悉,如果你第一次看代码,你会觉得是在代码中看到
QueMoveToNextChild(&(game->childrenQueue_))感 觉好些,还是看到一堆鬼画符似的结构体点点点的感觉好?”老C解释道,“意图,这里强调意图,因为使用了函数你一眼就可以看出程序执行的意图,而如果是一 堆代码的话,你还要反应半天;如果明白了意图,再去看代码,感觉会好很多——而且你可以根据代码意图提出更好的实现方法;同时这样也减少了代码中注释的工 作量——一般在维护代码的时候人们很少去修改注释的;最后,如果你的具体实现需要被维护,这样也给维护代码的人提供了线索,无需他在源代码程序中找来找 去……如果不小心还有可能将你的代码进行错误的修改……”他找到水杯,喝了一大口,“总之不要害怕小而短的函数,有时它们对阅读代码的人来说是很好的伙 伴……”
     “哦,有些道理。那么会不会影响程序执行的效率呢?”
     “……会有一些,不过你要理解20-80原则……”
     “什么是20-80原则?”小P问。
     “就是说影响程序执行效率的代码只占代码总量的20%,我们如果要提高效率,需要把80%的经历投放到这20%的代码上——一般来说都是一些算法、方案上 的问题。换句话说,除非需要,否则不要进行效率优化——可维护性要高于效率——再说我们这样做对效率的影响是微乎其微的。”
     “哦,”小P点点头,“为什么你的static函数命名这样奇特?”
     “唔……习惯。因为这些函数作用于Queue这个模块,所以我使用Que作为前缀,表示函数属于Queue模块,然后采用动宾结构进行命名……习惯、习惯而已。”老C笑道。
     “模块?什么是模块?”小P追问。
     “呵呵,这个是我们下来需要讨论的问题,和我们的V1.02版本有些关系。”老C说完又新建了一个AppleGame_V1.02的目录。
     两人抬头看看天色,已经接近黄昏了,于是老C决定暂停一下,“这个……我们还是先去喂脑袋吧……人是铁……”
     “呵呵,好吧,我知道一个东北菜馆还不错……我请,我请……”小P拍拍口袋。
     “呵呵,总让你请多不好意思,我请,我请……”两人一边推让,一边向门口走去。

(V1.02还在后面喔)

posted on 2009-02-04 22:10 Anderson 阅读(2033) 评论(9)  编辑 收藏 引用

评论

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三)[未登录] 2009-02-04 23:14 ypp

羡慕小P,可以有朋友交流,促进,不像我们自学的这种,恩,慢慢琢磨  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-02-05 02:01 imnobody

继续力挺老C  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-02-05 11:18 yzb

更着小P一起,
和老C加油.
挺啊,期待...  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三)[未登录] 2009-02-06 09:39 Sunny

不错.加油  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三)[未登录] 2009-02-06 22:16 Steven

不错,期待连载!  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-02-16 19:11 supersand

兄弟,写的不错,继续呀,等着呢。。。  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-02-17 08:04 tmhlcwp

好久没出新的了,等的急啊 。。。  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-03-01 02:06 宝宝阿涕

0.03版的main中怎么是include的applegame.h啊

好像应该是applegame.c才对样

这个不错哦,感觉复习一下C,大一只学了一点点,都忘了

喜欢老C的教学方式啊  回复  更多评论   

# re: 第一桶 从C到C++ 第八碗 陈老C演迭代开发 潘小P学渐进编程(之三) 2009-04-17 15:55 ty

受教了,对博主的景仰之情如黄河之水滔滔不绝,希望博主继续!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


<2009年2月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

导航

统计

常用链接

留言簿(6)

随笔档案(21)

文章档案(1)

搜索

最新评论

阅读排行榜

评论排行榜