Search code examples
cgetopt

Are optarg values persistent across successive calls to getopt?


By experiment, it appears that I can can capture successive values of optarg while iterating int getopt(int argc, char * const argv[], const char *optstring) and reference them later, as in the following sample program:

// 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;
}

Compilation & example execution:

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

Question: Is this valid? I.e. are successive non-NULL optarg values valid after successive iterations of getopt() and/or its final iteration (when it returns -1)? I.e. is it safe to capture successive values and reference them later (i.e. without strduping them)? I don't want to assume my experimental code is generally correct.

The man page states there is an extern char* optarg but doesn't specify whether it may be reused by successive invocations of getopt().

(Since the arguments to getopt are argc and argv, that implies that optarg is set to offsets of argv in which case I imagine it is safe to capture its successive values, but I'd like to learn if this is a correct surmise).


Solution

  • According to the 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.

    (Emphasis mine.)

    This says optarg always points into an element of argv. It never makes a copy.

    Since the elements of argv are valid for as long as your program runs, your code is valid (no copying required).


    NB. The POSIX page also shows an example of command line options with arguments that is essentially equivalent to your version:

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