Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Understanding char **argv

1 view
Skip to first unread message

kevin.e...@googlemail.com

unread,
Jul 5, 2008, 4:02:27 PM7/5/08
to
Hello all,

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?

Thanks.

Kevin

Richard Heathfield

unread,
Jul 5, 2008, 5:27:02 PM7/5/08
to
kevin.e...@googlemail.com said:

> Hello all,
>
> Forgive the somewhat simple question I am sure this will be, but I am
> having some problem understanding what: char **argv looks like.

argv is a pointer to the first element in an array of pointers to char,
where each pointer in the array points to the first character in a string.

> 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);

Replace that line with these two:

printf("Pointer: %p\tValue: %s\n", (void *)argv, *argv);
++argv;

<snip>

> Listed, but I would have expected to have needed to write:
>
> *(*(argv))

That's equivalent to **argv, and each iteration through the loop would give
you the first character of the relevant string for that iteration:

printf("Pointer: %p\tValue: %s\tFirst: %c\n",
(void *)argv,
*argv,
**argv);
++argv;

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999

Eric Sosman

unread,
Jul 5, 2008, 5:34:34 PM7/5/08
to

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:

char** char*'s char's

argv ---> argv[0] ---> { 'f', 'o', 'o', '\0' }
argv[1] ---> { 'o', 'n', 'e', '\0' }
argv[2] ---> { 't', 'w', 'o', '\0' }
argv[3] ---> { 't', 'h', 'r', 'e', 'e', '\0' }
argv[4] == NULL

--
Eric Sosman
eso...@ieee-dot-org.invalid

Joe Wright

unread,
Jul 5, 2008, 6:10:33 PM7/5/08
to

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 ---

kevin.e...@googlemail.com

unread,
Jul 5, 2008, 6:20:11 PM7/5/08
to
Eric --

On 5 Jul, 22:34, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
> char** char*'s char's
>
> argv ---> argv[0] ---> { 'f', 'o', 'o', '\0' }
> argv[1] ---> { 'o', 'n', 'e', '\0' }
> argv[2] ---> { 't', 'w', 'o', '\0' }
> argv[3] ---> { 't', 'h', 'r', 'e', 'e', '\0' }
> argv[4] == NULL

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.

Kevin

Ben Bacarisse

unread,
Jul 5, 2008, 6:32:50 PM7/5/08
to
"kevin.e...@googlemail.com" <kevin.e...@googlemail.com>
writes:

<snip>


> 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?

--
Ben.

viza

unread,
Jul 5, 2008, 6:53:18 PM7/5/08
to
On Sat, 05 Jul 2008 15:20:11 -0700, kevin.e...@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.

HTH
viza

kevin.e...@googlemail.com

unread,
Jul 5, 2008, 6:55:21 PM7/5/08
to
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.

Thanks!

Kevin

badc...@gmail.com

unread,
Jul 5, 2008, 7:36:11 PM7/5/08
to
kevin.e...@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;
}

Richard Heathfield

unread,
Jul 5, 2008, 8:04:08 PM7/5/08
to
badc...@gmail.com said:

> kevin.e...@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.

Keith Thompson

unread,
Jul 5, 2008, 9:04:09 PM7/5/08
to

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"

Joe Wright

unread,
Jul 5, 2008, 9:09:24 PM7/5/08
to
Richard Heathfield wrote:
> badc...@gmail.com said:
>
>> kevin.e...@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.
>
No, a string IS a contiguous sequence (an array) of characters
terminated by (and including) the first null character. A string is
referred to by the address (of type char*) of its first member.

CBFalconer

unread,
Jul 5, 2008, 10:39:42 PM7/5/08
to
"kevin.e...@googlemail.com" wrote:
>
... snip ...

>
> 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]

Because those don't have the same meaning. However you can freely
replace "char **argv" with "char *argv[]" in the parameter list.
Note the added '*'.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.


0 new messages