[C语言]你真的了解C语言吗之main函数(二)

在上篇文章中我们简单讨论了一下main()函数,了解了一下正确的写法,在文章的最后我们简单分析了一下参数是怎么传递的,但是想要把这些参数规规范范的整明白还是需要花一点时间的,本片文章只针对命令行参数处理函数getopt()做一定讨论,并无太高深的技术实现,所以大神可以直接拉到底点个赞走人了~啊哈哈哈哈,开玩笑,对行业内的鄙视链还是了解的,处于鄙视链底层的我没反驳的底气,哎~努力吧!

一般开发具有GUI(Graphical User Interface)的程序是不怎么太关注一些命令行的,而现在大多的应用程序都是具有图形界面的,使用者也很少对命令行有所了解的,就像我现在系统标配是ubuntu,但是很多情况下也是使用图形界面去完成一定操作的,很多时候在命令行中操作效率是非常高的。

其实getopt()函数跟main()函数并无太大的直接关系,但是当你写一个功能比较多的程序时,或多或少都会处理一点参数,这时候我们一般都会在main()函数里面使用getopt()去处理,我们可以理解为跟main()函数是相互关联的。我们先看一下这个函数到底是什么:

函数说明 getopt()用来分析命令行参数。参数argc和argv分别代表参数个数和内容,跟main()函数的命令行参数是一样的。参数 optstring为选项字符串, 告知 getopt()可以处理哪个选项以及哪个选项需要参数,如果选项字符串里的字母后接着冒号“:”,则表示还有相关的参数,全域变量optarg 即会指向此额外参数。如果在处理期间遇到了不符合optstring指定的其他选项getopt()将显示一个错误消息,并将全域变量optopt设为“?”字符,如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。

以上引自百度百科-getopt()

相关头文件:

#include <unistd.h> 
/**
 *@brief UNIX Standard
 *@description unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的
               头文件的名称。该头文件由 POSIX.1 标准(单一UNIX规范的基础)提出,故所有遵循
               该标准的操作系统和编译器均应提供该头文件
**/

相关声明及相关变量说明:

extern char *optarg;      /** getopt(3) external variables */
extern int optind, opterr, optopt;
int   getopt(int argc, char * const argv[], const char *optstring);
extern int optreset;      /** getopt(3) external variable */
/**
*@author imliubo
*为防止混淆,在本篇文章中对命令行参数进行分类,例“-p 22”,称带“-”为选项,不带“-”为选项参数或参数,
 特例“-p22”,有时选项和参数之间没有空格,此时“-”后面第一个字母为选项,第一个字母之后和下一个空格
 之前的称为选项参数或参数
*这些变量声明可以看出都是extern的,我们是可以在main函数中使用的,这些变量的值会随着getopt()函数
 的不断调用而不断变化的
 * *optarg  option argument,指向选项后面的实际参数(比如“-p 22”,getopt解析完成后,
            会将“22”的值赋给optarg)
 * optind   option index,argv中要处理的下一个选项参数的索引,当我们处理完一个参数时,此时
            argv[optind]会索引到下一个命令行中的选项或参数
 * opterr   option error,当opterr不为0时,会将无效选项或缺少参数的选项的信息输出到stderr,
            此时会将错误的选项储存到optopt,并返回“?”,当opterr为0时,不会输出任何信息,但是
            错误还是错误,只是不输出了而已
 * optopt   option option,当选项或参数有错误时,储存无效的选项或缺少参数的选项,无效的选项
            就是我们没有在optstring定义的,缺少选项参数就是选项应该加参数却没加
 * optreset option reset,从字面意思就可以看出是复位的意思,就是我们调用getopt一次后,可以
            将optreset置1,并且将optind重新初始化,此时前一次调用getopt函数所改变的变量
            都会恢复到初始化状态,然后可以重新调用getopt函数
*这些变量是我们需要传给getopt(),argc,argv就是我们在main函数声明时定义的,其中optstring是
 我们需要根据我们自己定义的参数去定义的
 * argc     来自main函数的入参
 * argv[]   来自main函数的入参
 * optstring 选项字符的集合,例":p:lv:d::",其中选项字符后面带“:”或“::”的都需要加参数,比如
        其中“p”,“v”,“d”选项都需要参数,不同的是带一个“:”的选项和参数之间可以使用或者不
        使用空格,带“::”的选项和参数之间是不能使用空格分隔的,选项字符后面没有跟“:”的是
             不可以加选项参数的,比如选项“l”,其中第一个“:”字符有或者没有时,解析错误会有不同
             的返回值,见下面
*返回值
 * x   解析成功返回选项字符,x表示optstring中的任意选项字符
 * -1  解析完成,返回-1,可以用来判断是否还有选项去处理
  *optstring 第一个字符为“:”
   * ?  解析错误,无效选项,此时无效选项的值存储在optopt中
   * :   解析错误,选项缺少参数,此时缺少参数的选项的值存储在optopt中
  *optstring 第一个字符不为“:”
   * ?   解析错误,无效选项或者选项缺少参数都会返回“?”,此时无效选项的值或者缺少参数选项的值存储
         在optopt中
*/

下面我们先来一个简单的测试程序尝试一下:

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

int main(int argc, char *argv[])
{
   int opt;
   while ((opt = getopt(argc, argv, ":ihm::b:")) != -1) {
       //printf("opt: %c  optarg: %s  optopt: %c  optind: %d\n",(char)opt,optarg,(char)optopt,optind);
       switch (opt) {
       case 'i':
          printf("作者:IAMLIUBO\n");
          break;
       case 'm':
          printf("专栏:%s\n",optarg);
          break;
       case 'b':
          printf("主页:%s\n",optarg);
          break;
       case 'h':
          fprintf(stderr,"Usage: [Options] [value]\n\n");
          fprintf(stderr,"  -i            printf author\n");
          fprintf(stderr,"  -h            printf help\n");
          fprintf(stderr,"  -m [value]    printf 专栏:value\n");
          fprintf(stderr,"  -b [value]    printf 主页:value\n");
          break;
       case ':':
          printf(">>%c<<选项缺少参数\n",(char)optopt);
          break;
       case '?':
          printf(">>%c<<是无效选项\n",(char)optopt);
          break;
       }
   }

   return 0;
}

这个是optstring第一个字符为":"的,所以错误信息应该会区分无效选项或选项缺少参数,其中"i","h"选项是不需要参数的,"m"选项后面有两个":",所以选项与参数之前不应该有空格,"b"选项后面有一个":",所以选项与参数之间有无空格都可以,我们看实际测试效果:

image
  • 第一个是正确的命令,可以看到打印都正确
  • 第二个是"m"选项与参数之间有空格,所以专栏的名称就没打印出来
  • 第三个是测试一个无效选项"o",可以看到optstring中是没有这个选项的,所以打印无效选项
  • 第四个是选项"b"没有跟上实际的参数,所以打印选项缺少参数
  • 第五个是打印帮助命令,在optstring中选项"h"后面没有":",所以不需要参数

我们再测试一下optstring第一个选项字符不为":"的,下面是测试程序:

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

int main(int argc, char *argv[])
{
   int opt;
   opterr = 0;
   while ((opt = getopt(argc, argv, "ihm::b:")) != -1) {
       //printf("opt: %c  optarg: %s  optopt: %c  optind: %d\n",(char)opt,optarg,(char)optopt,optind);
       switch (opt) {
       case 'i':
          printf("作者:IAMLIUBO\n");
          break;
       case 'm':
          printf("专栏:%s\n",optarg);
          break;
       case 'b':
          printf("主页:%s\n",optarg);
          break;
       case 'h':
          fprintf(stderr,"Usage: [Options] [value]\n\n");
          fprintf(stderr,"  -i            printf author\n");
          fprintf(stderr,"  -h            printf help\n");
          fprintf(stderr,"  -m [value]    printf 专栏:value\n");
          fprintf(stderr,"  -b [value]    printf 主页:value\n");
          break;
       case '?':
          fprintf(stderr, "Usage: %s [-i] [-h] [-m\"专栏名称\"] [-b \"主页网址\"]\n",
                   argv[0]);
          break;
       }
   }

   return 0;
}

image

可以看出与上面不同的是,不管是无效选项或者选项缺少参数都会返回"?",从而打印一样的内容(第三跟第四),所以这是optstring中第一个字符是否为":"的区别,我们可以利用这一点区别为使用程序的用户提供更精确的提示,所以建议大家还是使用第一种。

大家如果想看每解析一次各个变量值的变化可以将注释掉的内容取消注释,这样就会每解析一次便打印一次各个变量的值。

扩展:

  • POSIX标准

POSIX_百度百科​baike.baidu.com

  • "-","--"," "的区别

我们有时候会见到命令行选项前面有的是一个"-"跟一个选项字符,有的是"--"跟一个单词,还有时候什么都没有直接跟选项字符,那么这到底有什么不同呢?其实本质是一样的,主要是风格不同,Unix风格是一个"-"跟上一个选项字符,GUN风格是"--"跟上一个单词,BSD风格是不加任何"-",但是他们之间都是兼容的,所以只是形式不同,本质上还是一样的。

对main函数的重新认识暂时就到这里,这里只是利用getopt函数简单写了个demo,其实只要解析出了选项的参数,后面大家对不同的参数做不同的处理应该也很简单了,比如写个命令行计算器?虽然人家有写好的,但是这也不妨碍大家写一个更好的嘛~

文章难免有错误,如果有不对之处,还请及时指出我好及时改正。

欢迎关注我的专栏,我会不定期分享一些开发经验。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,306评论 0 5
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,618评论 0 10
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,387评论 3 44
  • 有时候学过的知识反过头来再看看总会有新理解,这可能就是所谓的温故而知新吧,最近读了一篇文章,觉得说的很对,感兴趣的...
    IAMLIUBO阅读 1,010评论 0 0
  • Leon and Amy: 有个朋友知道我开了个淘宝店,就特意在我的店下订单买东西。她的老公和我一样姓邹,而他的淘...
    岭南后生阅读 440评论 0 0