用下面的骨架程序做实验,找出答案。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s COMMAND\n", argv[0]);
return EXIT_FAILURE;
}
if (!strcmp(argv[1], "foo")) {
printf("Doing foo.\n");
} else
if (!strcmp(argv[1], "bar")) {
printf("Doing bar.\n");
} else {
fprintf(stderr, "Unknown command line parameter '%s'.\n", argv[1]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
通知实用程序用户做什么的最常用方法是不带参数运行实用程序,或者使用-h 或--help 作为唯一参数。 (Windows 命令行实用程序可能使用 /? 或类似的。)
假设用户可以通过以下方式运行编译后的程序program:
./program list
./program tree
./program -h
./program --help
./program
第一种形式告诉程序使用链表;第二种形式告诉程序使用树;而其他形式只是输出usage,如何调用程序的信息:
Usage: ./program [ -h | --help ]
./program MODE
Where MODE is one of:
list Linked-list mode
tree Tree mode
Further details on what the program actually does...
您只需很少的代码即可实现这一目标:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
enum {
NO_MODE = 0,
LIST_MODE,
TREE_MODE
};
int main(int argc, char *argv[])
{
int mode = NO_MODE;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("Usage: %s [ -h | --help ]\n", argv[0]);
printf(" %s MODE\n", argv[0]);
printf("\n");
printf("Where MODE is one of\n");
printf(" list for linked list mode\n");
printf(" tree for tree mode\n");
printf("\n");
printf("Further details on what the program actually does...\n");
printf("\n");
return EXIT_SUCCESS;
}
if (!strcmp(argv[1], "list"))
mode = LIST_MODE;
else
if (!strcmp(argv[1], "tree"))
mode = TREE_MODE;
else {
fprintf(stderr, "%s: Unknown MODE.\n", argv[1]);
return EXIT_FAILURE;
}
/* mode == LIST_MODE or TREE_MODE here,
depending on the first command line parameter.
*/
return EXIT_SUCCESS;
}
请注意,|| 运算符在 C 中是短路的:如果左侧为假,则根本不计算右侧。因此,上面的第一个strcmp() 检查仅在argv == 2 时进行,第二个在argv == 2 和第一个strcmp() 返回非零(不匹配)时进行。
也就是说,usage部分的正文只在argv != 2时运行(少于两个,或多于两个命令行项,将程序名计为一个);或者如果唯一的命令行参数匹配-h 或--help。
! 是 C 中的 not 运算符。当且仅当 x 为零或 NULL 时,!x 的计算结果为 1;否则发给0。
(您可以通过使用!!x 来迷惑人们。如果x 为零,则计算为零,如果x 不为零,则为一。这是合乎逻辑的。通常称为非非操作。)
enum 只是提醒您魔术常数不好;最好使用枚举或预处理器宏(#define NO_MODE 0 等)。在一个地方使用1 来表示树模式,而在另一个地方使用2 会非常容易;这样的错误很难调试,需要人类阅读代码的太多注意力才能找到这样的错误。所以不要使用魔法常量,而是使用枚举或宏。
上面,我决定NO_MODE的值为零,并让编译器将值分配(增加)给LIST_MODE和TREE_MODE;将它们视为编译时整数常量。 (意思是,您可以在switch 语句中的case 标签中使用它们。)
因为strcmp() 在两个字符串匹配时返回零,所以当且仅当argv[1] 包含字符串baz 时!strcmp(argv[1], "baz")) 为真(非零)。在比较字符串时,您总是在实际代码中看到它。
如果您在此处查看我的答案,您会经常在我的示例代码中看到 if (argc ...) "usage" 块。这是因为即使是我自己也会经常在几天之内忘记该计划的确切目的是什么。我通常在我的机器上编写了几十个示例程序,而不是查看源代码来查看是否有什么东西让我记忆犹新,我只是简单地运行示例 sn-ps 而不使用命令行参数(或者实际上,使用 @987654359 @ 参数,因为有些是过滤器),看看它们做了什么。速度更快,阅读量更少,我会更快找到相关的sn-p。
总之,在你的所有程序中编写一个用法输出块,尤其是当它只是一个你不会在任何地方发布的测试程序时。它们很有用,特别是当你有一个充满它们的库时,各种代码 sn-ps(每个都在它们自己的目录中;我使用一个四位数字和一个简短的描述性名称)来实现有趣或有用的东西。从长远来看,它可以节省时间和精力,任何让我变得高效和懒惰的东西在我的书中都是好的。