Forgive the somewhat simple question I am sure this will be, but I am having some problem understanding what: char **argv looks like. For instance, i know through trial and error that if I do this:
#include <stdio.h>
int main(int argc, char *argv[]) { int l; for( l=0; l<argc; l++ ) { printf("Pointer: %p\tValue: %s\n", argv++, *argv); }
return 0;
}
And i invoke the above as:
./foo one two three
Then I will see:
0x10202 foo 0x10204 one 0x10208 two ox1020c three
Listed, but I would have expected to have needed to write:
*(*(argv))
So as to dereference what the pointer that *argv pointed to. Or have I missed the point?
> Forgive the somewhat simple question I am sure this will be, but I am > having some problem understanding what: char **argv looks like. For > instance, i know through trial and error that if I do this:
> 0x10202 foo > 0x10204 one > 0x10208 two > ox1020c three
> Listed, but I would have expected to have needed to write:
> *(*(argv))
> So as to dereference what the pointer that *argv pointed to. Or have > I missed the point?
Peel the onion one layer at a time.
`argv' is a pointer to a pointer to a char, which I'll write as "pointer to [pointer to char]" for emphasis.
Applying `*' to a pointer accesses the thing the pointer points to. So `*argv' is a "[pointer to char]", the thing that `argv' itself points at.
Now we apply `*' again, this time to the "[pointer to char]" we got from the first step. `**argv' (which is the same a `*(*argv))') is therefore a char, the first char of one of the argument strings.
Another thing you can do to gain facility in understanding multiple levels of pointers is to draw a picture:
> Forgive the somewhat simple question I am sure this will be, but I am > having some problem understanding what: char **argv looks like. For > instance, i know through trial and error that if I do this:
> 0x10202 foo > 0x10204 one > 0x10208 two > ox1020c three
> Listed, but I would have expected to have needed to write:
> *(*(argv))
> So as to dereference what the pointer that *argv pointed to. Or have > I missed the point?
Maybe two points.
1. The parens are not needed. 'char **argv' is what it is, a pointer to a pointer to char.
2. In your printf statement above you have both argv++ and *argv. Because we have no way to know which expression might be evaluated first, this is classic Undefined Behavior.
#include <stdio.h>
int main(int argc, char **argv) { int l; for (l = 0; l < argc; l++) { printf("Pointer: %p\tValue: %s\n", argv, *argv); argv++; } return 0;
}
..is the way I would do it. -- Joe Wright "Everything should be made as simple as possible, but not simpler." --- Albert Einstein ---
If *only* one of the books I've been reading had this in it, I wouldn't be as confused -- thank you very much for that, and to the others in this thread who've replied.
I suppose understand conceptually how type_t **foo works -- i do have to ask *why* the command-line options to a program is in the form:
char **foo
As opposed to a singular array of chars, i,e,:
char foo[SOME_MAX_VALUE]
My guess is: char **foo defers the decision to decide how big the array might be at runtime, but I could be wrong.
Thanks once again for everyone's patience -- I know this is heavy going.
> I suppose understand conceptually how type_t **foo works -- i do have > to ask *why* the command-line options to a program is in the form:
> char **foo
> As opposed to a singular array of chars, i,e,:
> char foo[SOME_MAX_VALUE]
> My guess is: char **foo defers the decision to decide how big the > array might be at runtime, but I could be wrong.
I doubt it. The best argument is that it is the "right thing to do". Imagine your version. Every program would have to decide itself how to parse the command into parts. A program that operates on files can just run through the elements of argv doing its job on each one. If it got one long string, how does it decide what is a file name?
On Sat, 05 Jul 2008 15:20:11 -0700, kevin.eugen...@googlemail.com wrote: > I suppose understand conceptually how type_t **foo works -- i do have to > ask *why* the command-line options to a program is in the form:
> char **foo
> As opposed to a singular array of chars, i,e,:
> char foo[SOME_MAX_VALUE]
> My guess is: char **foo defers the decision to decide how big the array > might be at runtime, but I could be wrong.
That is two separate questions.
q1) why is it char* and not char[SOME_MAX_VALUE] ?
a1) It allows implementors to only use the minimum required or to use some maximum value if they prefer. The downside is that the user programmer cannot rely on being able to write past the end of the string, but that's not a problem because he can define his own MAX_VALUE and create his own automatic array if he needs it.
q2) Why is it char** and not char* ?
a2) Because that would greatly limit the power of shells, and it would require every application to perform its own command line interpreting (eg: working out that 'some string' is one argument without the quotes).
As an aside, on many implementations there is no specific limit on the length of any one argument, but the total of all of them is limited. Here IIRC that limit is 32kiB.
On 5 Jul, 23:32, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> I doubt it. The best argument is that it is the "right thing to do". > Imagine your version. Every program would have to decide itself how > to parse the command into parts. A program that operates on files can > just run through the elements of argv doing its job on each one. If > it got one long string, how does it decide what is a file name?
But if it were just an array holding type_t:
type_t foo[BAR];
Then BAR could still be evaluated at run-time -- much like argc is to main() surely? So again: Why the need for multiple indirection -- I am not saying you're wrong, I am just curious why we have it for command-line options in this case, and perhaps where other use of: type_t **foo come into play.
kevin.eugen...@googlemail.com wrote: > So again: Why the need for multiple indirection > [...] I am just curious why we have it for > command-line options in this case [...]
Because there is no "string" type in C. If C had a string type (which I'll call 'tstring'), main could probably be declared as
int main(int argc, tstring argv[])
where argv is an array of tstring.
But ... strings in C are represented by char *.
============= #include <stdio.h> typedef char * tstring; int main(int argc, tstring argv[]) { int n; for (n=0; n<argc; n++) { printf("arg %d is \"%s\".\n", n, argv[n]); } return 0;
> kevin.eugen...@googlemail.com wrote: >> So again: Why the need for multiple indirection >> [...] I am just curious why we have it for >> command-line options in this case [...]
> Because there is no "string" type in C. > If C had a string type (which I'll call 'tstring'), > main could probably be declared as
> int main(int argc, tstring argv[])
So far, so good.
> where argv is an array of tstring.
No, it would be a pointer to the first element in such an array.
> But ... strings in C are represented by char *.
No, they are represented by a contiguous sequence of characters terminated by the first null character. They can be pointed to by char *, in the sense that a char * can point at their first member.
"kevin.eugen...@googlemail.com" <kevin.eugen...@googlemail.com> writes: > Forgive the somewhat simple question I am sure this will be, but I am > having some problem understanding what: char **argv looks like. For > instance, i know through trial and error that if I do this:
> 0x10202 foo > 0x10204 one > 0x10208 two > ox1020c three
> Listed, but I would have expected to have needed to write:
> *(*(argv))
> So as to dereference what the pointer that *argv pointed to. Or have > I missed the point?
argv is of type char**, so *argv is of type char*.
printf() with the "%s" format expects an argument of type char*, which must point to the first character of a string. printf itself will dereference this char* pointer to extract and print the characters of the string.
Incidentally, "%p" is the correct format for printing a pointer value, but it expects an argument of type void*. If you want to print a pointer of some other type, you should cast it to void*.
Finally, some style points (oh, here goes Keith with his "style points" again).
"l" isn't a great name for a variable; it looks too much like the digit 1 (and can be indistinguishable in some fonts).
Some would argue that modifying a function argument is poor style. I don't agree, but modifying argv while leaving argc alone is odd. An idiom I've seen for traversing command-line arguments is to increment argv while decrementing argc. Or you can just use an integer to loop through the arguments, leaving argc and argv alone. (If you're concerned that indexing will be slower than stepping through with a pointer, don't be; the difference will be minimal, and a good optimizing compiler will likely generate the same code either way.)
Of course none of this is a big deal for a program this small, but small programs are where you should start to develop good habits.
-- Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst> Nokia "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister"