当使用`getopt_long`函数时,理解`option long_options[]`是什么意思?

7

我正在学习使用getopt_long。从维基百科示例 2(使用GNU扩展getopt_long)中,我看到了以下代码:

#include <stdio.h>     /* for printf */
#include <stdlib.h>    /* for exit */
#include <getopt.h>    /* for getopt_long; POSIX standard getopt is in unistd.h */
int main (int argc, char **argv) {
    int c;
    int digit_optind = 0;
    int aopt = 0, bopt = 0;
    char *copt = 0, *dopt = 0;
    static struct option long_options[] = {
        {"add", 1, 0, 0},
        {"append", 0, 0, 0},
        {"delete", 1, 0, 0},
        {"verbose", 0, 0, 0},
        {"create", 1, 0, 'c'},
        {"file", 1, 0, 0},
        {NULL, 0, NULL, 0}
    };
    int option_index = 0;
    while ((c = getopt_long(argc, argv, "abc:d:012",
                 long_options, &option_index)) != -1) {
        int this_option_optind = optind ? optind : 1;
        switch (c) {
        case 0:
            printf ("option %s", long_options[option_index].name);
            if (optarg)
                printf (" with arg %s", optarg);
            printf ("\n");
            break;
        case '0':
        case '1':
        case '2':
            if (digit_optind != 0 && digit_optind != this_option_optind)
              printf ("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf ("option %c\n", c);
            break;
        case 'a':
            printf ("option a\n");
            aopt = 1;
            break;
        case 'b':
            printf ("option b\n");
            bopt = 1;
            break;
        case 'c':
            printf ("option c with value '%s'\n", optarg);
            copt = optarg;
            break;
        case 'd':
            printf ("option d with value '%s'\n", optarg);
            dopt = optarg;
            break;
        case '?':
            break;
        default:
            printf ("?? getopt returned character code 0%o ??\n", c);
        }
    }
    if (optind < argc) {
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
            printf ("%s ", argv[optind++]);
        printf ("\n");
    }
    exit (0);
}

我不太理解option long_options[]对象。 第一列 我认为long_options[]的第一列应该是用户在命令行中使用的长标志(即跟在--后面的内容)。 第二列 我原本认为第二列应该只包含no_argumentrequired_argumentoptional_argument,但实际上我看到的是0和1。 第三列 我不理解第三列的含义。 第四列和最大标志数 第四列是用于switch语句的唯一标识符。然而,如果唯一标识符只能是一个字符,那么我们是否限制于所有小写字母(26个)+所有大写字母(26个)+数字(10个)+可能一些特殊字符,总共不超过62个不同的参数?这是getopt的限制吗?如果我理解有误,请问如何在第三个参数中指定超过两个字符以标识标志(例如""abc:d:012"")?
我想option long_options[]的最后一行是用于当getopt返回-1时,因此实际上并不重要,只要它存在即可。

1
C++不是C语言... - amanuel2
我想你指的是我使用了标签 C。那是个错误,我本意是使用 C++。(标签已更正)。谢谢。 - Remi.b
@Remi.b,你第一次标记为C++。amanuel2编辑了你的问题,将C++标签更改为C。代码在任何一种语言中都可以工作。如果你想特别将其理解为C++代码,则C++标签是适当的,但请注意该代码并不展示C++最佳实践,你可能会收到相关评论。 - user743382
1
维基百科不是手册;getopt_long() 的哪个部分在引起问题? - Jonathan Leffler
2
没必要猜测struct option成员的含义。请参考真正的 getopt_long() 文档,可以在许多地方找到,显然不包括维基百科。例如,在这里,或者 这里,更不用说通过man命令在您自己的计算机上查找了。 - John Bollinger
显示剩余2条评论
1个回答

14

struct option数组在man getopt_long中被准确定义[注1],以下是摘录:

longopts是指向在<getopt.h>中声明的struct option数组的第一个元素的指针。

   struct option {
       const char *name;
       int         has_arg;
       int        *flag;
       int         val;
   };
不同字段的含义如下: name 是长选项的名称。 has_arg 表示长选项是否需要参数,取值可以是 no_argument(或 0),表示该选项不需要参数;required_argument(或 1),表示该选项必须有参数;或 optional_argument(或 2),表示该选项可以有可选参数。 flag 指定了一个长选项的返回结果方式。如果 flagNULL,则 getopt_long() 函数返回 val 的值(例如,调用程序可以将 val 设置为等效的短选项字符)。否则,getopt_long() 返回 0,并且 flag 指向一个变量,如果找到了该选项,则设置为 val,但如果没有找到该选项,则保持不变。 val 是要返回的值,或者要加载到 flag 所指向的变量中的值。
数组的最后一个元素必须填充为零。
因此,您通常会使用第二个元素的符号常量(has_arg),但 man 手册允许您使用 0、1 或 2,可能是为了向后兼容。IMHO,Wikipedia 应该使用符号常量,但这是 Wikipedia 和编辑之间的事情。 getopt_long 返回一个 int,而不是一个 char。如果第三个字段(即 flag)为 NULL(或等效地为 0),则将返回第四个字段的值 val,它可以是适合于 int 的任何内容。字符肯定适合 int,因此您可以返回与短选项字符等效的值(如手册中所述),但您无需这样做。getopt 也返回一个 int,但由于它总是返回一个选项字符(或错误指示),因此它永远不会返回许多 int 值。
如果第三个字段不是 NULL,则预计其指向一个 int 类型的变量,getopt_long 将在其中存储 val 的值。例如,这可以用于布尔标志。
enum FROBNICATE { FROB_UNSET = -1, FROB_NO = 0, FROB_YES = 1 };
/* ... */

/* This is conceptually an enum, but `getopt_long` expects an int */
int frob_flag = FROB_UNSET;

struct option long_opts = {
  /* ... */
  {"frobnicate", no_argument, &frob_flag, FROB_YES},
  {"unfrobnicated", no_argument, &frob_flag, FROB_NO},
  /* ... */
  {NULL, 0, NULL, 0}
};

/* Loop over arguments with getopt_long;
   In the switch statement, you can ignore the returned value
   0 because the action has been fully realized by setting the
   value of a flag variable.
 */

if (frob_flag == FROB_UNSET)
  frob_flag = get_default_frobnication();

如manpage所示,数组中的最后一个条目必须为所有零(或指针成员的NULL)。这是必需的,以便getopt_long知道数组在哪里结束。

注释

  1. 您可能已经在系统上安装了manpages,因此只需键入man getopt_long即可查看getopt_long的文档。对于任何标准C库函数、GNU libc函数和一般情况下安装了-doc包的C库函数,这应该都有效。(强烈推荐)总的来说,在查看维基百科之前,应首先尝试使用manpage,因为manpage将是实际安装在系统上的库函数版本的文档。

  2. 函数返回给定数据类型并不意味着它可能返回该数据类型的任何可能值。


在C++中,将枚举用作标志似乎不起作用。我收到了一个“错误:无法将'FROBNICATE *'转换为'int *'”。我该如何解决这个问题? - Mike
在我看来,g++ 应该会从未作用域的枚举类型自动转换为 int*。即使将类型明确设置为 int 也不起作用:enum FROBNICATE : int { FROB_UNSET = -1, FROB_NO = 0, FROB_YES = 1 }; - Mike
1
@Mike:我的错,而且在C++中这将更加明显,因为它对枚举类型的要求更加严格(正确)。由于option::flag的类型是int*,所以存储标志值的变量需要是int类型,即使其概念上是一个枚举类型。如果您在C++中使用类型安全的枚举,则还需要添加转换,这将很麻烦。我在答案中更改了类型,但没有转换(因为问题仍然是C)。谢谢。 - rici

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接