Modbus軟件開發實戰指南 之 開發自己的Modbus Poll工具 - 2

接上一篇文章的內容。

看了前面需求提到的複雜的命令行解析功能,很多人立馬開始發怵,其實大可不必。

我們都知道,Linux下的程序往往都提供了複雜的命令行參數處理機制,因爲這是與

其他程序或用戶進行交互的主要手段,在這樣的情況下難能可貴的是,爲了減輕開發

人員對命令行處理的負擔,Linux提供了系統函數getopt()或getopt_long()專門解析命令行參數。

 

在Linux系統中,函數getopt()/getopt_long()位於 unistd.h 系統頭文件中,其原型分別爲:

int getopt(int argc,char * const argv[],const char * optstring);

int getopt_long(int argc, char * const argv[],const char *optstring,

                const struct option *longopts, int *longindex);

其中,參數argc和argv是由主函數main()傳遞的參數個數和內容。

參數optstring 則代表欲處理的選項字符串。此函數會返回在argv 中下一個的選項字母,

此字母會對應參數optstring 中的字母。如果選項字符串裏的字母后接着冒號“:”,則表示還有相關的參數,

全域變量optarg 即會指向此額外參數。如果getopt()找不到符合的參數則會打印出錯信息,並將全域

變量optopt設爲“?”字符,如果不希望getopt()打印出錯信息,則只要將全域變量opterr設爲0即可。

 

參數可簡單劃分爲短參數和長參數兩種類型,getopt()使用optstring所指的字串作爲短參數列表,

象“1ac:d::”就是一個短參數列表。短參數的定義是一個'-'後面跟一個字母或數字,象-a, -b就是一個

短參數,每個數字或字母定義一個參數。

 

而長參數則形如“--debug”,前面有2個'-'符號,後面可添加多個字母或數字。

getopt_long()函數包含了getopt()函數的功能,並且還可以指定“長參數”(或者說長選項),

與getopt()函數對比,getopt_long()比getopt()多了兩個參數。

 

此函數的基本用法如下(Linux下):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#include <stdio.h>
#include <unistd.h>

int main(int argc, int *argv[])
{
    int ch;
    opterr = 0;

    // getopt()可由getopt_long()替換
    while ((ch = getopt(argc, argv, "a:bcde")) != -1)
    {
        switch(ch)
        {
        case 'a':
            printf("option a:'%s'\n", optarg);
            break;
        case 'b':
            printf("option b :b\n");
            break;
        default:
            printf("other option :%c\n", ch);
        }
    }
    printf("optopt +%c\n", optopt);
}

以上作爲參照,可見調用函數getopt()或getopt_long()可以非常方便地解析命令行。

但是,有一點遺憾的是,如此方便的函數在Windows下卻沒有提供,怎麼辦呢?當然有辦法了,

既然函數getopt()/getopt_long()是GNU C中的函數,那麼源碼可見就可以根據情況直接移植到Windows下。

說幹就幹,接下來簡要介紹一下移植方法,掌握一點新技能,如果對這部分沒有興趣,可以跳過,看後面的內容。

 

首先,訪問GNU C Library (glibc)的主頁http://www.gnu.org/software/libc/,並下載最新的glibc庫,

當前最新版是glibc-2.24.tar.gz,下載完畢並解壓。

提取加壓後的目錄\glibc-2.24\posix\下的4個源文件getopt.h/getopt.c/getopt_int.h/

 

getopt_init.c,如圖所示。

 

 

 圖 提取getopt()相關文件

啓動Visual Studio 2015,選擇菜單【File】->【New】->【Project...】,

準備創建一個新的默認工程項目,項目類型爲【Visual C++】→【Win32 Console Application】。

創建新的默認工程項目完畢之後,切換到資源管理器畫面,將以上4個文件複製到新項目所在目錄,並添加到工程項目中,如圖所示。

圖 添加getopt()源文件

文件添加完畢之後,我們試着編譯一下看看,果不其然,文件getopt.c出現了編譯錯誤:

getopt.c(71): fatal error C1083: Cannot open include file: 'gettext.h': No such file or directory

首先需要修改的是沒有“gettext.h”這個頭文件的問題。修改方法爲直接將其註釋掉或刪除,然後修改後面的宏定義。

將下面的原始代碼(大概在70行):

1
2
3
4
5
6

#ifdef _LIBC
# include <libintl.h>
#else
# include "gettext.h"
# define _(msgid) gettext (msgid)
#endif

修改爲:

1
2
3
4
5

#ifdef _LIBC
# include <libintl.h>
#else
# define _(msgid)  (msgid)
#endif

修改完畢,繼續編譯一下看看,出現如下編譯錯誤,如圖所示。

                    

圖 編譯錯誤alloca無法識別

 

錯誤的文字描述爲:

getopt.c(568): warning C4013: 'alloca' undefined; assuming extern returning int

error LNK2019: unresolved external symbol _alloca referenced in function __getopt_internal_r

可以發現,這裏出錯的原因是alloca這個函數沒有定義,那麼alloca函數是什麼意思呢?

原來alloca是一個內存分配函數,與malloc、calloc、realloc類似,但是注意一個重要的區別,

alloca函數是在棧(stack)上申請空間,用完馬上就釋放。

 

一般情況下,函數alloca包含在頭文件malloc.h中,在某些系統中被定義爲內部函數_alloca的宏定義。

既然已經知道原型了,那麼修改alloca爲_alloca即可解決問題,如圖所示。

 

圖 修改爲_alloca解決編譯錯誤

 

繼續添加getopt_long()/getopt_long_only()的定義,這兩個函數在getopt.h文件中聲明瞭,

但是其定義在getopt1.c中,可以直接將getopt1.c文件也拿過來用,但是因爲這個文件中的內容不多,

爲了減少文件的數量,直接將其中有用的部分拷貝到getopt.c文件中是個不錯的主意。

文件getopt1.c中要拷貝的內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

int
getopt_long (int argc, char *const *argv, const char *options,
             const struct option *long_options, int *opt_index)
{
    return _getopt_internal (argc,argv,options,long_options,opt_index,0,0);
}

int
_getopt_long_r (int argc, char *const *argv, const char *options,
                const struct option *long_options, int *opt_index,
                struct _getopt_data *d)
{
    return _getopt_internal_r(argc,argv,options,long_options,opt_index,
                               0, d, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (int argc, char *const *argv, const char *options,
                  const struct option *long_options, int *opt_index)
{
    return _getopt_internal(argc,argv,options,long_options,opt_index,1,0);
}

int
_getopt_long_only_r (int argc, char *const *argv, const char *options,
                     const struct option *long_options, int *opt_index,
                     struct _getopt_data *d)
{
    return _getopt_internal_r(argc,argv,options,long_options,opt_index,
                               1, d, 0);
}

將以上代碼拷貝到文件getopt.c中函數getopt()定義之後即可,修改完畢編譯,一切OK!

至此函數getopt()移植結束。經過上面的修改,可以進行一些簡單的測試進行驗證,

測試用例不用自己寫了,在文件getopt.c和getopt1.c文件中都有,直接拿過來用就可以。

至此,重新生成的4個文件:getopt.h/getopt.c/getopt_int.h/getopt_init.c就是需要的命令行解析源代碼文件,可以用在Windows系統下。

 

至此,針對自己開發modbus poll工具的命令行解析功能基本實現了。

接下來,將進行功能部分的代碼分析和調試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章