在 Xcode 中新建一个 macOS 平台 Command Line Tool,使用 C 语言,会发现 main 带了两个参数:argc
和argv
。如下:
argc
被程序唤起的命令行参数的数量,用于统计参数数量。这里会导致你有一个误区,在下面argv[]
部分细说。需要注意的是,虽然一些程序支持-abcd
这种合在一起的程序,但是 C 会将其认为是一个,需要手动去改。
argv
这里需要细致说一下,之前写的太简洁了,评论有人没看懂,有一些底层逻辑需要说明一下。
之前写的版本为:
argv
是一个指向一个字符串数组的指针,数组包含了参数,每个字符串就是一个参数,最后一个元素为0
。不过一般习惯使用多级指针来操作字符串。
现在看来确实太简洁了,而且很多人对于数组和指针搞不太清楚,下面详细解释一下,最后进行反省思考。
char *argv[]
表示argv[]
是一个存放字符类型元素地址的数组,因为 C 中是有字符串的概念的:将每个字符存放在 char 数组,最后一个元素为\0
表示字符串的结束。printf(%s)
就是输出字符串。并且一般使用argv
指针来访问、处理argv[]
数组的内容。
C语言中,数组等价于一个指针加偏移量。所以可以把argv
当作指向一个指针数组argv[]
的指针,不用定义,直接可以用。(指针的核心在于“指”,比如说a
是个指针,那么它会指向一块空闲的内存,然后使用这部分内存。而数组,比如这里的argv
则是直接是数组的位置)
在argv[]
数组中存放的的指针指向输入命令的各部分(调用程序、选项、参数)。Shell 中输入的命令通过判断空格进行分割,然后将字符串的头地址放入argv[]
数组中,最后放入一个0
,表示空指针。
比如说如果 Shell 输入为echo hello, world!
,那么argv[]
数组存放的指针有 4 个:
- 值为
echo\0
头地址的指针; - 值为
hello,\0
头地址的指针; - 值为
world!\0
头地址的指针; - 空指针
0
。
像这条调用程序后面直接就是参数的很容易判断,直接数组第二个开始即可。
但是很多程序是要判断情况的,也就是选项,开发者应该怎么判断选项和参数呢?
一般习惯使用多级指针来操作字符串。前辈们很聪明,利用这点发明了“UNIX 风格”的命令,比如说ls -s .
这种命令,选项前面加一个横杠-
,这样就可以区分开二者了。
按照 UNIX 的代码惯例,上来直接跳过第一个,然后判断指针指向的字符串第一个字符是不是-
,如果是的,那么进入一个switch
判断,用case
列出多种支持的情况下,应该执行什么代码。例如下面这样就可以判断选项和处理参数:
int c;
while (--argc > 0 && (*++argv)[0] == '-' {
while (c = *++argv[0] {
switch(c){
case 'x':
...
break;
case 'n':
...
break;
default:
printf("xxx: illegal opyion %c\n", c);
...
break;
}
}
}
最后逐句思考一下自己之前的版本为什么会有人看不懂。原文截图如下:
“argv
是一个指向一个字符串数组的指针,数组包含了参数”:“argv”这种对于很多人来说就是数组本身。但是一般情况下,如果单独说“argv”,指的是数组的指针。如果指的是数组,需要用“数组 argv”、“argv 数组”、“argv[]
”来表示。因为前面说到过,数组和指针关系十分密切,所以原文我加了代码样式,如果在代码中,argv
就直接表示为指针。并且一般代码中,数组是直接通过指针来使用的(这样的好处是长度不用预先设置好)。但是在我也问了一下我朋友之后,才知道很多人不清楚这点,以后还是要详细说明一下这种东西。
“每个字符串就是一个参数,最后一个元素为0
”: 这句话的问题在于有一点歧义,加上省略了太多东西(看上面的解释也看得出来)。我的意思“程序运行时,会自动将从输入的获取的字符串分割成多个字符串,然后将这些字符串存放在 argv 数组,数组中最后一个元素被存放为空指针0
”。导致歧义的原因是使用了从句。在中文中使用从句确实会导致这个问题,以后注意。
“不过一般习惯使用多级指针来操作字符串”: 这个属于我只当笔记记了,这个如果没有看过一些源码的人根本看不懂在说什么东西,心理估计是“你说这个谁懂啊.mp4”。所以以后还是要详细说明一下,当作记录。不然我现在记得什么情况,过了一两年自己也看不懂了。
希望以后博客当中不要再出这些问题了,对于之前打开这篇博客没得到解决的人说一声抱歉。
2023-11-23 更新:
最近发现了一种新方法,写了篇博客《C语言获取命令行选项(option/flag)和输入的内容的一种巧妙的办法》