作业:代码中的软件工程
代码中的软件工程
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

3.3 vscode安装c/c++插件

3.4 配置文件设置
.vscode 文件夹
- 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
}
]
}
]
}
- 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"
}
]
}
- 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 环境搭建测试
- 编写测试文件
#include<stdio.h>
void main(){
printf("hello,world!\n");
}
- 编译运行

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 编写菜单头文件及其实现
- menu.h
/* add cmd to menu */
int MenuConfig(char *cmd, char *desc, int (*handler)());
/* Menu Engine Execute */
int ExecuteMenu();
- 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 编译运行


浙公网安备 33010602011771号