Difference between (*++argv)[0] and while(c = *++argv[0])
I have the following snippet of code:
int main(int argc, char *argv[])
{
char line[MAXLINE];
long lineno = 0;
int c, except = 0, number = 0, found = 0;
while(--argc > 0 && (*++argv)[0] == '-') //These two lines
while(c = *++argv[0]) //These two lines
switch(c) {
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("find: illegal option %c\n", c);
argc = 0;
found = -1;
break;
}
...
}
Containing the following expressions:
while(--argc > 0 && (*++argv)[0] == '-')
Does this expression in the parentheses (*++argv)[0]
differ from while(c = *++argv[0])
without parentheses?
If so, how? Does (*++argv)
mean pointer to the next argument, and does *++argv[0]
mean pointer to the next 开发者_运维知识库character in the current char array which is being pointed to?
First, K&R have an errata on this particular snippet:
117(§5.10): In the find example, the program increments
argv[0]
. This is not specifically forbidden, but not specifically allowed either.
Now for the explanation.
Let's say your program is named prog
, and you execute it with: prog -ab -c Hello World
. You want to be able to parse the arguments to say that options a
, b
and c
were specified, and Hello
and World
are the non-option arguments.
argv
is of type char **
—remember that an array parameter in a function is the same as a pointer. At program invocation, things look like this:
+---+ +---+---+---+---+---+
argv ---------->| 0 |-------->| p | r | o | g | 0 |
+---+ +---+---+---+---+---+
| 1 |-------->| - | a | b | 0 |
+---+ +---+---+---+---+
| 2 |-------->| - | c | 0 |
+---+ +---+---+---+---+---+---+
| 3 |-------->| H | e | l | l | o | 0 |
+---+ +---+---+---+---+---+---+
| 4 |-------->| W | o | r | l | d | 0 |
+---+ +---+---+---+---+---+---+
| 5 |-------->NULL
+---+
Here, argc
is 5, and argv[argc]
is NULL
. At the beginning, argv[0]
is a char *
containing the string "prog"
.
In (*++argv)[0]
, because of the parentheses, argv
is incremented first, and then dereferenced. The effect of the increment is to move that argv ---------->
arrow "one block down", to point to the 1
. The effect of dereferencing is to get a pointer to the first commandline argument, -ab
. Finally, we take the first character ([0]
in (*++argv)[0]
) of this string, and test it to see if it is '-'
, because that denotes the start of an option.
For the second construct, we actually want to walk down the string pointed to by the current argv[0]
pointer. So, we need to treat argv[0]
as a pointer, ignore its first character (that is '-'
as we just tested), and look at the other characters:
++(argv[0])
will increment argv[0]
, to get a pointer to the first non- -
character, and dereferencing it will give us the value of that character. So we get *++(argv[0])
. But since in C, []
binds more tightly than ++
, we can actually get rid of the parentheses and get our expression as *++argv[0]
. We want to continue processing this character until it's 0
(the last character box in each of the rows in the above picture).
The expression
c = *++argv[0]
assigns to c
the value of the current option, and has the value c
. while(c)
is a shorthand for while(c != 0)
, so the while(c = *++argv[0])
line is basically assigning the value of the current option to c
and testing it to see if we have reached the end of the current command-line argument.
At the end of this loop, argv will point to the first non-option argument:
+---+ +---+---+---+---+---+
| 0 |-------->| p | r | o | g | 0 |
+---+ +---+---+---+---+---+
| 1 |-------->| - | a | b | 0 |
+---+ +---+---+---+---+
| 2 |-------->| - | c | 0 |
+---+ +---+---+---+---+---+---+
argv ---------->| 3 |-------->| H | e | l | l | o | 0 |
+---+ +---+---+---+---+---+---+
| 4 |-------->| W | o | r | l | d | 0 |
+---+ +---+---+---+---+---+---+
| 5 |-------->NULL
+---+
Does this help?
Incrementing argv is a very bad idea, as once you have done so it is difficult to get the original value back. It is simpler, clearer and better to use an integer index - after all argv IS an array!
To answer your question ++argv increments the pointer. This then has indirection applied to it to get the first character.
yes, you are correct.
while(--argc > 0 && (*++argv)[0] == '-')
is scanning the array (of length argc) of command line arguments one by one looking for those starting with a -
option prefix. For each of those:
while(c = *++argv[0])
is scanning through the set of switch characters that follow the first -
in the current argument (i.e. t
and n
in -tn
, until it hits the string null terminator \0
, which terminates the while loop, since it evaluates as false.
This design allows both
myApp -t -n
and
myApp -tn
to both work and be understood as having the options t
and n
.
The parentheses change the order in which the expressions are evaluated.
Without parentheses *++argv[0]
:
argv[0]
gets the pointer to character data currently pointed to byargv
.++
increments that pointer to the next character in the character array.*
gets the character.
with parentheses (*++argv)[0]
:
++argv
increments the argv pointer to point to the next argument.*
defereferences it to obtain a pointer to the character data.[0]
gets the first character in the character array.
Yes, the two expressions differ (though only slightly). IMO, this code is a bit on the excessively clever side. You'd be better off with something like this:
for (int i=1; i<argc; i++)
if (argv[i][0] == '-') {
size_t len = strlen(argv[i]);
for (int j=0; j<len; ++j)
switch(argv[i][j]) {
case 'x':
// ...
This is pretty much equivalent to the code above, but I doubt anybody (who knows C at all) would have any difficulty figuring out what it really does.
精彩评论