Reputation: 6295
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 strdup
ing 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).
Upvotes: 5
Views: 1035
Reputation: 85897
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:
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.
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;
...
Upvotes: 5
Reputation: 6279
The man page for getopt states:
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
And also:
By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.
This means, it does not allocate any memory and does not store text in static buffers, but instead works directly on the argv
pointer array you provided and just gives you the address within it.
You can verify this behavior in code:
#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;
}
Upvotes: 0