ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

menu实验代码的分析

2020-10-28 19:32:20  阅读:179  来源: 互联网

标签:函数 int menu 代码 cmd 接口 pNode 实验 tDataNode


致谢

首先感谢孟宁老师的教学指导。这篇文章主要基于孟宁老师上课的内容完成。

仔细阅读分析源代码,结合代码分析其中的软件工程方法、规范或软件工程思想。具体要求如下:
对模块化设计、可重用接口、线程安全等议题结合代码进行理解和分析;

vscode c++环境配置

vscode安装c/c++扩展以MAC为例

  1. 点击1号位置
  2. 在2号位置输入c搜索
  3. 安装如图扩展

下载Mingw-w64/GCC编译器

为了在不同环境下保持一致,我们选择Mingw-w64/GCC
brew install gcc gdb # for macOS
可能需要运行xcode-select —install 和brew update
sudo apt install build-essential gdb # for Ubuntu Linux
可能需要运行sudo apt update
https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe for windows

配置vscode配置文件

  1. 进入一个目录,创建一个cpp文件,我创建的文件如下
  2. 使用快捷键command+shift+p,再输入edit回车生成如下文件
    • 原始值为
{
    "configurations": [
        {
            "name": "Mac",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "macFrameworkPath": [
                "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
            ],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}
  • 替换为:
{
  "configurations": [
    {
      "name": "Mac",
      "includePath": [
        "${workspaceFolder}/**",
        "/Library/Developer/CommandLineTools/usr/include/c++/v1",
        "/usr/local/include",
        // "/Library/Developer/CommandLineTools/usr/lib/clang/9.0.0/include",
        "/Library/Developer/CommandLineTools/usr/include"
        // "/usr/include"
      ],
      "defines": [],
      "macFrameworkPath": ["/System/Library/Frameworks", "/Library/Frameworks"],
      "compilerPath": "/usr/bin/clang",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "clang-x64"
    }
  ],
  "version": 4
}
  1. command+shift+p打开命令行工具窗口,输入或者选择Tasks: Configure Task
  • 原始值为:
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "msbuild",
            "args": [
                // Ask msbuild to generate full paths for file names.
                "/property:GenerateFullPaths=true",
                "/t:build",
                // Do not generate summary otherwise it leads to duplicate errors in Problems panel
                "/consoleloggerparameters:NoSummary"
            ],
            "group": "build",
            "presentation": {
                // Reveal the output only if unrecognized errors occur.
                "reveal": "silent"
            },
            // Use the standard MS compiler pattern to detect errors, warnings and infos
            "problemMatcher": "$msCompile"
        }
    ]
}
  • 替换为
{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "c++",
      "command": "clang++",
      "type": "shell",
      "args": ["./c++/hello.cpp", "-std=c++11", "-g"],
      "presentation": {
        "echo": true,
        "reveal": "always",
        "focus": false,
        "panel": "shared"
      }
    }
  ]
}

  1. 修改launch.json
  • 原始值为:
{
    // 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": "(lldb) 启动",
            "type": "cppdbg",
            "request": "launch",
            "program": "输入程序名称,例如 ${workspaceFolder}/a.out",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "lldb"
        }
    ]
}
  • 替换为:
{
  // 使用 IntelliSense 了解相关属性。
  // 悬停以查看现有属性的描述。
  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "c/c++ Launch",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/a.out",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": true,
      "MIMode": "lldb",
      "preLaunchTask": "c++"
    }
  ]
}

更具体配置可参考:https://code.visualstudio.com/docs/cpp/config-clang-mac

源码分析

  • 代码结构

    圈出来的c源码文件是主要起作用的文件,后面的test开头的c文件都是具体使用对应模块进行自定义menu的案例。
    linktable文件主要操作对应数据指针
  • 执行过程
    先在项目下使用make命令编译文件,然后执行一个test案例即可./test这里执行的是这个。执行之后界面如下:
  • 有如下定制的命令
  • quitversion命令测试

软件工程方法和规范

  • 函数声明和定义
    声明:函数功能的描述
    定义:函数的具体实现

  • 函数声明和定义分开放置的好处
    编译速度:将所有包含的文件连接在一起然后进行解析时,减少包含文件中代码的数量和复杂性将缩短编译时间。
    避免代码重复/内联:如果您在头文件中完全定义了一个函数,则包含该头并引用该函数的每个目标文件都将包含该函数的自身版本。附带说明一下,如果要进行内联,则需要将完整的定义放入头文件中(在大多数编译器中)。
    封装/清晰度:一个定义良好的类/函数集以及一些文档应足以供其他开发人员使用您的代码。 (理想情况下)不需要他们了解代码的工作原理-那么为什么要求他们筛选代码呢? (当然,相反的说法是,当需求仍然存在时,对于他们访问实现可能会很有用)。

在此模块中的具体体现就是:在.h文件中进行函数声明,在.c文件中进行具体的函数实现。如下图:
menu.h

menu.c

  • 规范化的注释

注释和版权信息:注释也要使用英文,不要使用中文或特殊字符,要保持源代码是ASCII字符格式文件;
不要解释程序是如何工作的,要解释程序做什么,为什么这么做,以及特别需要注意的地方;
每个源文件头部应该有版权、作者、版本、描述等相关信息。
- 在代码中的具体体现:

  • 函数与变量命名规范

不同语言都有其命名规范,C语言变量命名是驼峰式的,在老师的代码中体现的淋漓尽致。

  • 模块化的软件设计
    1.2 数据结构的设计
    把每个操作提取出其共同点,作为一个节点
    typedef struct DataNode { tLinkTableNode * pNext; char* cmd; char* desc; int (*handler)(int argc, char *argv[]); } tDataNode;
    所有操作汇聚成一条链,总共就组成了这个menu的自定义命令菜单。
struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int			SumOfNode;
    pthread_mutex_t mutex;

};
      ```
这里还增加了一个锁,增加了操作的安全性。

      2.1 内聚度与耦合度
      内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性(Feather)。
      在这个模块设计中如何体现:
      - 体现1:
      ![](https://www.icode9.com/i/l/?n=20&i=blog/2164312/202010/2164312-20201028191500636-1183978996.png)
      `linktable`这两个文件中主要是对操作节点链的处理。每增加或者删除一个操作,只要调用这两个文件里面的函数即可,而不用再自己写了。
      增加了复用性,降低了耦合性。
      - 体现2:

      ```
/* 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 = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
    }
    return 0;
}

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

int SetPrompt(char * p)
{
    if (p == NULL)
    {
        return 0;
    }
    strcpy(prompt,p);
    return 0;
}
/* add cmd to menu */
int MenuConfig(char * cmd, char * desc, int (*handler)())
{
    tDataNode* pNode = NULL;
    if ( head == NULL)
    {
        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("%s",prompt);
        /* 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)
        {
            continue;
        }
        printf("%s - %s\n", p->cmd, p->desc);
        if(p->handler != NULL) 
        { 
            p->handler(argc, argv);
        }
    }
} 
      ```

这里主要是把添加的菜单项放入menu中。功能集中,内聚度低,里面的其他功能直接复用上面已经定义的节点操作,耦合度低。
      2.2 接口模块
      软件模块接口在面向过程的语言中一般是定义一些数据结构和函数接口API,在面向对象的编程语言中一般在类或接口类中定义一些公有的(public)属性和方法。两类编程语言中接口形式上有很大不同,但是不管是函数接口API还是公有的方法本质上都是函数定义。我们将重点介绍两种函数接口方式,即Call-in方式的函数接口和Callback方式的函数接口。这里我们先来理解函数接口规格。

      具体体现:
      `tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);`
      该接口的目标是从链表中取出链表的头节点,函数名GetLinkTableHead清晰明确地表明了接口的目标;
      该接口的目标是从链表中取出链表的头节点,函数名GetLinkTableHead清晰明确地表明了接口的目标;
      该接口的前置条件是链表必须存在使用该接口才有意义,也就是链表pLinkTable != NULL;
      使用该接口的双方遵守的协议规范是通过数据结构tLinkTableNode和tLinkTable定义的;
      使用该接口之后的效果是找到了链表的头节点,这里是通过tLinkTableNode类型的指针作为返回值来作为后置条件,C语言中也可以使用指针类型的参数作为后置条件;
      该接口没有特别要求接口的质量属性,如果搜索一个节点可能需要在可以接受的延时时间范围内完成搜索;

标签:函数,int,menu,代码,cmd,接口,pNode,实验,tDataNode
来源: https://www.cnblogs.com/shizi-4/p/13890512.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有