optarg 值在对 getopt 的连续调用中是否持久?

Are optarg values persistent across successive calls to getopt?

通过实验,我似乎可以在迭代 int getopt(int argc, char * const argv[], const char *optstring) 时捕获 optarg 的连续值并在以后引用它们,如以下示例程序所示:

// main.c

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

int main( int argc, char* argv[] )
{
  int opt;
  char o;
  char* a = NULL;
  char* b = NULL;

  while ( -1 != ( opt = getopt( argc, argv, "abcd:e:" ) ) )
  {
    char o = opt & 0xFF;

    switch ( o )
    {
      case 'a':
      {
        printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
        break;
      }
      case 'b':
      {
        printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
        break;
      }
      case 'c':
      {
        printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
        break;
      }
      case 'd':
      {
        printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
        a = optarg;
        break;
      }
      case 'e':
      {
        printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
        b = optarg;
        break;
      }
    }
  }

  printf( "(%p): [%s]\n", a, (NULL == a ? "" : a ) );
  printf( "(%p): [%s]\n", b, (NULL == b ? "" : b ) );

  return 0;
}

编译&示例执行:

> gcc -g main.c && ./a.out -dabc -e def -a
d (0x7fffe8d1d2b2): [abc]
e (0x7fffe8d1d2b9): [def]
a ((nil)): []
(0x7fffe8d1d2b2): [abc]
(0x7fffe8d1d2b9): [def]

问题:这个有效吗? IE。在 getopt() and/or 的最后一次迭代(当它 returns -1 时)的连续迭代之后,连续的非 NULL optarg 值是否有效? IE。捕获连续的值并在以后引用它们是否安全(即没有 strduping 它们)?我不想假设我的实验代码通常是正确的。

手册页指出有一个 extern char* optarg 但没有指定它是否可以被连续调用 getopt().

(因为 getopt 的参数是 argcargv,这意味着 optarg 设置为 argv 的偏移量,在这种情况下我想象捕获它的连续值是安全的,但我想知道这是否是正确的推测)。

根据POSIX specification of getopt

The getopt() function shall return the next option character (if one is found) from argv that matches a character in optstring, if there is one that matches. If the option takes an argument, getopt() shall set the variable optarg to point to the option-argument as follows:

  1. If the option was the last character in the string pointed to by an element of argv, then optarg shall contain the next element of argv, and optind shall be incremented by 2. If the resulting value of optind is greater than argc, this indicates a missing option-argument, and getopt() shall return an error indication.

  2. Otherwise, optarg shall point to the string following the option character in that element of argv, and optind shall be incremented by 1.

(强调我的。)

这表示 optarg 总是指向 argv 的元素。它从不复制。

由于 argv 的元素在您的程序运行期间一直有效,因此您的代码是有效的(无需复制)。


注意。 POSIX 页面还显示了一个 example of command line options with arguments,它基本上等同于您的版本:

char *ifile;
...
while ((c = getopt(argc, argv, ":abf:o:")) != -1) {
    switch(c) {
    ...
    case 'f':
        ifile = optarg;
        break;
    ...

getopt 的手册页指出:

optstring is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argu‐ ment, so getopt() places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg

还有:

By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.

这意味着,它不分配任何内存,也不在静态缓冲区中存储文本,而是直接在您提供的 argv 指针数组上工作,只为您提供其中的地址。

您可以在代码中验证此行为:

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

int main(int argc, char **argv) {

    int opt, cur;

    while (-1 != (opt = getopt(argc, argv, "a:b:c:"))) {
        cur = optind - 1;
        printf("current elemnt = %d argv[%d] = %p optarg = %p delta = %d\n",
            cur, cur, argv[cur], optarg, (int)(optarg - argv[cur]));
    }

    return 0;
}