作业:代码中的软件工程

代码中的软件工程

1.任务要求及参考资料

以VS Code + GCC工具集为主要环境编译调试课程项目案例;修改后的地址
仔细阅读分析源代码,结合代码分析其中的软件工程方法、规范或软件工程思想。具体要求如下:

  • 完成编译和调试环境配置;
  • 对模块化设计、可重用接口、线程安全等议题结合代码进行理解和分析;
  • 根据如https://gitee.com/mengning997/se/blob/master/src/lab7.3/linktable.h 接口编写内部实现和测试驱动,要求使用callback方式查询。

参考资料:软件工程 —— 码农的自我修养

2.获取源码

通过网站给定链接,下载相应源码。
https://gitee.com/mengning997/se/blob/master/ppt/menu_code.zip

3.构建编译环境

3.1 下载安装vscode

下载地址:
https://code.visualstudio.com/#alt-downloads

安装:sudo apt install ./<file>.deb

3.2 安装gcc和gdb

image

3.3 vscode安装c/c++插件

image

3.4 配置文件设置

.vscode 文件夹

  1. launch.json
    • Debugger Settings
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gdb",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}
  1. tasks.json
    • Build Instructions
{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "C/C++: gcc build active file",
			"command": "/usr/bin/gcc",
			"args": [
				"-g",
				"${file}",
				"-o",
				"${fileDirname}/${fileBasenameNoExtension}"
			],
			"options": {
				"cwd": "${workspaceFolder}"
			},
			"problemMatcher": [
				"$gcc"
			],
			"group": "build",
			"detail": "compiler: /usr/bin/gcc"
		}
	]
}
  1. c_cpp_properties.json
    • Compiler path and IntelliSense settings
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "gnu17",
            "cppStandard": "gnu++14",
            "intelliSenseMode": "gcc-x64"
        }
    ],
    "version": 4
}

3.5 环境搭建测试

  1. 编写测试文件
#include<stdio.h>

void main(){
    printf("hello,world!\n");
}
  1. 编译运行

image

4. 菜单小程序实现

4.1 linktable.h接口文件


/********************************************************************/
/* Copyright (C) SSE-USTC, 2012-2021                                */
/*                                                                  */
/*  FILE NAME             :  linktabe.h                             */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :  LinkTable                              */
/*  MODULE NAME           :  LinkTable                              */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/12/30                             */
/*  DESCRIPTION           :  interface of Link Table                */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/12/30
 * modified by Mengning, 2021/04/23
 *
 */

#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_

#define SUCCESS 0
#define FAILURE (-1)

/*
 * LinkTable Node Head Type, example as below:
 * typedef struct UserNode
 * {
 *     tLinkTableNode head;
 *     tUserData data;
 * }tUserNode;
 */
typedef struct LinkTableNode tLinkTableNode;

/*
 * LinkTable Type
 */
typedef struct LinkTable tLinkTable;

/*
 * Create a LinkTable
 */
tLinkTable * CreateLinkTable();
/*
 * Delete a LinkTable
 */
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
 * Add a LinkTableNode to LinkTable
 */
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * Delete a LinkTableNode from LinkTable
 */
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * Search a LinkTableNode from LinkTable
 * int Conditon(tLinkTableNode * pNode,void * args);
 */
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args);

/*
 * get LinkTableHead
 */
tLinkTableNode *GetLinkTableHead(tLinkTable *pLinkTable);

#endif /* _LINK_TABLE_H_ */

4.2 linktable.c具体实现

/* SA20225203 黄鹏 */

#include "linktable.h"
#include <stdio.h>
#include <stdlib.h>

#include <pthread.h>

/*
 * LinkTableNode Type
 */
struct LinkTableNode
{
    tLinkTableNode *pNext;
};

/*
 * LinkTable Type
 */
struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int SumOfNode;
    pthread_mutex_t mutex;
};

/*
 * Create a LinkTable
 */
tLinkTable *CreateLinkTable()
{
    tLinkTable *pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable)); // 申请连表头节点
    if (pLinkTable == NULL)
    {
        return NULL;
    }
    // 初始化头节点信息
    pLinkTable->pHead = NULL;
    pLinkTable->pTail = NULL;
    pLinkTable->SumOfNode = 0;
    // 设置互斥锁,使用默认属性设置
    pthread_mutex_init(&(pLinkTable->mutex), NULL);
    return pLinkTable;
}

/*
 * Delete a LinkTable
 */
int DeleteLinkTable(tLinkTable *pLinkTable)
{
    if (pLinkTable == NULL)
    {
        return FAILURE;
    }
    // 循环释放链表所有节点
    while (pLinkTable->pHead != NULL)
    {
        tLinkTableNode *p = pLinkTable->pHead;
        pthread_mutex_lock(&(pLinkTable->mutex));     // 加锁
        pLinkTable->pHead = pLinkTable->pHead->pNext; // 设置头节点指针指向链表首节点之后的部分
        pLinkTable->SumOfNode -= 1;                   // 修改链表长度
        pthread_mutex_unlock(&(pLinkTable->mutex));   // 释放锁
        free(p);                                      // 释放删除节点的空间
    }
    // 将头节点数据恢复初始化
    pLinkTable->pHead = NULL;
    pLinkTable->pTail = NULL;
    pLinkTable->SumOfNode = 0;
    pthread_mutex_destroy(&(pLinkTable->mutex)); // 销毁互斥锁
    free(pLinkTable);                            // 释放头节点空间
    return SUCCESS;
}

/*
 * Add a LinkTableNode to LinkTable
 */
int AddLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode)
{
    if (pLinkTable == NULL || pNode == NULL)
    {
        return FAILURE;
    }
    pNode->pNext = NULL; // 初始化待添加节点的next指针
    pthread_mutex_lock(&(pLinkTable->mutex));
    // 添加第一个节点
    if (pLinkTable->pHead == NULL)
    {
        pLinkTable->pHead = pNode;
    }
    if (pLinkTable->pTail == NULL)
    {
        pLinkTable->pTail = pNode;
    }
    else
    { // 在链表末尾添加新元素(尾插法)
        pLinkTable->pTail->pNext = pNode;
        pLinkTable->pTail = pNode;
    }
    pLinkTable->SumOfNode += 1; // 更新链表长度
    pthread_mutex_unlock(&(pLinkTable->mutex));
    return SUCCESS;
}

/*
 * Delete a LinkTableNode from LinkTable
 */
int DelLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode)
{
    if (pLinkTable == NULL || pNode == NULL)
    {
        return FAILURE;
    }
    pthread_mutex_lock(&(pLinkTable->mutex));
    // 删除节点位于链表首部
    if (pLinkTable->pHead == pNode)
    {
        pLinkTable->pHead = pLinkTable->pHead->pNext;
        pNode->pNext = NULL; // 将删除的节点从链表上拆下来
        pLinkTable->SumOfNode -= 1;
        if (pLinkTable->SumOfNode == 0) // 链表只有一个节点
        {
            pLinkTable->pTail = NULL;
        }
        pthread_mutex_unlock(&(pLinkTable->mutex));
        return SUCCESS;
    }
    tLinkTableNode *pTempNode = pLinkTable->pHead;
    while (pTempNode != NULL) // 遍历链表寻找符合条件节点的前一个节点。
    {
        if (pTempNode->pNext == pNode)
        {
            pTempNode->pNext = pTempNode->pNext->pNext;
            pNode->pNext = NULL; // 将删除的节点从链表上拆下来
            pLinkTable->SumOfNode -= 1;
            if (pTempNode->pNext == NULL) // 删除的节点为链表的最后一个元素,则修改头节点尾指针指向
            {
                pLinkTable->pTail = pTempNode;
            }
            pthread_mutex_unlock(&(pLinkTable->mutex));
            return SUCCESS;
        }
        pTempNode = pTempNode->pNext;
    }
    pthread_mutex_unlock(&(pLinkTable->mutex));
    return FAILURE; // 删除节点不在链表中
}

/*
 * Search a LinkTableNode from LinkTable
 * int Conditon(tLinkTableNode * pNode);
 */
tLinkTableNode *SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode *pNode, void *args), void *args)
{
    if (pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode *pNode = pLinkTable->pHead; // 遍历节点
    while (pNode != NULL)
    {
        if (Conditon(pNode, args) == SUCCESS) // Conditon 用于定义节点的比较方式
        {
            return pNode;
        }
        pNode = pNode->pNext;
    }
    return NULL;
}

/*
 * get LinkTableHead
 */
tLinkTableNode *GetLinkTableHead(tLinkTable *pLinkTable)
{
    if (pLinkTable == NULL)
    {
        return NULL;
    }
    return pLinkTable->pHead;
}

4.3 编写菜单头文件及其实现

  1. menu.h
/* add cmd to menu */
int MenuConfig(char *cmd, char *desc, int (*handler)());

/* Menu Engine Execute */
int ExecuteMenu();
  1. menu.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "linktable.h"
#include "menu.h"

tLinkTable *head = NULL; // 定义全局头节点指针

int Help(); // 帮助; 展示所有命令信息

#define CMD_MAX_LEN 1024
#define CMD_MAX_ARGV_NUM 32
#define DESC_LEN 1024
#define CMD_NUM 10

/* data struct and its operations */

typedef struct DataNode // 链表实际节点域
{
    tLinkTableNode *pNext;
    char *cmd;                              //指向命令字符串
    char *desc;                             // 命令描述
    int (*handler)(int argc, char *argv[]); // 命令对应的处理方法
} tDataNode;

int SearchConditon(tLinkTableNode *pLinkTableNode, void *arg)
{
    char *cmd = (char *)arg; // 获取目标字符串的地址
    tDataNode *pNode = (tDataNode *)pLinkTableNode;
    if (strcmp(pNode->cmd, cmd) == 0) // 比较命令字符串是否相等
    {
        return SUCCESS;
    }
    return FAILURE;
}

/* show all cmd in listlist */
int ShowAllCmd(tLinkTable *head)
{
    tDataNode *pNode = (tDataNode *)GetLinkTableHead(head); // 获取链表首部
    while (pNode != NULL)
    {
        printf("%s - %s\n", pNode->cmd, pNode->desc);
        pNode = pNode->pNext;
    }
    return 0;
}

int Help(int argc, char *argv[])
{
    ShowAllCmd(head);
    return 0;
}

/* add cmd to menu */
int MenuConfig(char *cmd, char *desc, int (*handler)(int argc, char *argv[]))
{
    tDataNode *pNode = NULL;
    if (head == NULL) // 添加第一条命令: help 指令 (只添加一次)
    {
        head = CreateLinkTable();
        pNode = (tDataNode *)malloc(sizeof(tDataNode));
        pNode->cmd = "help";
        pNode->desc = "Menu List:";
        pNode->handler = Help;
        AddLinkTableNode(head, (tLinkTableNode *)pNode);
    }
    pNode = (tDataNode *)malloc(sizeof(tDataNode));
    pNode->cmd = cmd;
    pNode->desc = desc;
    pNode->handler = handler;
    AddLinkTableNode(head, (tLinkTableNode *)pNode);
    return 0;
}

/* Menu Engine Execute */
int ExecuteMenu()
{
    /* cmd line begins */
    while (1)
    {
        int argc = 0;
        char *argv[CMD_MAX_ARGV_NUM];
        char cmd[CMD_MAX_LEN];
        char *pcmd = NULL;
        printf("Input a cmd > ");
        /* scanf("%s", cmd); */
        pcmd = fgets(cmd, CMD_MAX_LEN, stdin); // 从标准输入获取命令及参数
        if (pcmd == NULL)
        {
            continue;
        }
        /* convert cmd to argc/argv */
        pcmd = strtok(pcmd, " "); // 以空格分割成字符串,并获取第一个字符串指针
        while (pcmd != NULL && argc < CMD_MAX_ARGV_NUM)
        {
            argv[argc] = pcmd;
            argc++;
            pcmd = strtok(NULL, " ");
        }
        if (argc == 1)
        {
            int len = strlen(argv[0]);
            *(argv[0] + len - 1) = '\0'; // 为命令字符串添加尾零
        }
        tDataNode *p = (tDataNode *)SearchLinkTableNode(head, SearchConditon, (void *)argv[0]);
        if (p == NULL)
        {
            printf("This is a wrong cmd!\n ");
            continue;
        }

        if (p->handler != NULL)
        {
            p->handler(argc, argv);
        }
    }
}

4.4 测试文件编写

// test.c
#include <stdio.h>
#include <stdlib.h>
#include "menu.h"

int Quit(int argc, char *argv[])
{
    /* add XXX clean ops */
    exit(0);
}

int main(int argc, char *argv[])
{

    MenuConfig("version", "XXX V1.0(Menu program v1.0 inside)", NULL);
    MenuConfig("quit", "Quit from XXX", Quit);

    ExecuteMenu();
}

4.5 Makefile编写

#
# Makefile for Menu Program
#

CC_PTHREAD_FLAGS			 = -lpthread
CC_FLAGS                     = -c 
CC_OUTPUT_FLAGS				 = -o
CC                           = gcc
RM                           = rm
RM_FLAGS                     = -f

TARGET  =   test
OBJS    =   linktable.o  menu.o test.o

all:	$(OBJS)
	$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)

.c.o:
	$(CC) $(CC_FLAGS) $<

clean:
	$(RM) $(RM_FLAGS)  $(OBJS) $(TARGET) *.bak

4.6 编译运行

image

image

posted @ 2021-04-29 08:25  南冥丶  阅读(235)  评论(0)    收藏  举报