Notation of **argv in main function [duplicate]
Asked Answered
S

4

6

Possible Duplicate:
argc and argv in main

I'm having difficulty understanding the notation used for the general main function declaration, i.e. int main(int argc, char *argv[]). I understand that what is actually passed to the main function is a pointer to a pointer to char, but I find the notation difficult. For instance:

Why does **argv point to the first char and not the whole string? Likewise, why does *argv[0] point to the same thing as the previous example.

Why does *argv point to the whole first string, instead of the first char like the previous example?

This is a little unrelated, but why does *argv + 1 point a string 'minus the first char' instead of pointing to the next string in the array?

Stencil answered 13/11, 2012 at 15:9 Comment(5)
argv as array of strings: char* argv[] arv[0] is the first string, argv[0][0] is the first byte of the first string etc.Chinchin
There are no "strings" in C, just arrays of characters. Perhaps you're confused about what "pointing to the whole string" means.Succentor
You need to understand two unrelated pieces of basic C syntax: 1) in function arguments, T[] is the same as T*. 2) For a pointer p, p[i] is identical to *(p + i), and also &*p is identical to p. Use that to reflect on your question.Succentor
Sounds like you're having some issues understanding pointers vs. arrays. Take a look at this Q&A to see if it helps clear that up at least.Spear
I think much of why I'm getting thrown off with this is misunderstanding regarding order of operations.Stencil
N
11

Consider a program with argc == 3.

   argv
     |
     v
+---------+         +----------------+
| argv[0] |-------->| program name\0 |
+---------+         +-------------+--+
| argv[1] |-------->| argument1\0 |
+---------+         +-------------+
| argv[2] |-------->| argument2\0 |
+---------+         +-------------+
|    0    |
+---------+

The variable argv points to the start of an array of pointers. argv[0] is the first pointer. It points at the program name (or, if the system cannot determine the program name, then the string for argv[0] will be an empty string; argv[0][0] == '\0'). argv[1] points to the first argument, argv[2] points to the second argument, and argv[3] == 0 (equivalently argv[argc] == 0).

The other detail you need to know, of course, is that array[i] == *(array + i) for any array.

You ask specifically:

  • Why does **argv point to the first char and not the whole string?

*argv is equivalent to *(argv + 0) and hence argv[0]. It is a char *. When you dereference a char *, you get the 'first' character in the string. And **argv is therefore equivalent to *(argv[0]) or *(argv[0] + 0) or argv[0][0].

(It can be legitimately argued that **argv is a character, not a pointer, so it doesn't 'point to the first char'. It is simply another name for the 'p' of "program name\0".)

  • Likewise, why does *argv[0] point to the same thing as the previous example.

As noted before, argv[0] is a pointer to the string; therefore *argv[0] must be the first character in the string.

  • Why does *argv point to the whole first string, instead of the first char like the previous example?

This is a question of convention. *argv points at the first character of the first string. If you interpret it as a pointer to a string, it points to 'the whole string', in the same way that char *pqr = "Hello world\n"; points at 'the whole string'. If you interpret it as a pointer to a single character, it points to the first character of the string. Think of it as like wave-particle duality, only here it is character-string duality.

  • Why does *argv + 1 point a string 'minus the first char' instead of pointing to the next string in the array?

*argv + 1 is (*argv) + 1. As already discussed, *argv points at the first character of the first string. If you add 1 to a pointer, it points at the next item; since *argv points at a character, *argv+1 points to the next character.

*(argv + 1) points to the (first character of the) next string.

Northeastward answered 13/11, 2012 at 15:30 Comment(3)
This is very helpful, thank you for the response. You've helped a lot with my understanding, at least I think so. So the reason why the whole string gets printed out in say printf with *argv is because printf takes char *, so it prints out the whole string?Stencil
Yes. If you use a %s format string, then printf("%s\n", *argv); prints the whole string. If you use printf("%p\n", (void *)*argv);, then you'll get just the address printed. (I should apologize that I'm not sure whether 'first' is used consistently; it usually means 'subscript 0'.)Northeastward
Okay, thanks again for the effort put into your answer!Stencil
P
3

It all falls down to pointer arithmetic.

*argv[0] = *(*(argv + 0)) = **argv

Since [] has higher precedence than unary *.

On the other hand, *argv gives the first cell in the array, an array containing pointers. What does this pointer point to? Why a char array, a string, of course.

*argv + 1 gives what it gives because + has lower precedence than unary *, so first we get a pointer to a string, and than we add 1 to it, thus getting a pointer the the second character in the string.

Polypoid answered 13/11, 2012 at 15:18 Comment(0)
S
3

I understand that what is actually passed to the main function is a pointer to a pointer to char

No, what's passed is an array of char pointers (an array of character strings). Think of it like this, if I give this at the command prompt:

>> ./program hello 456

My program's main will get:

argc == 3

argv[0] == program (the name of the program as a string)
argv[1] == hello   (the first parameter as a string)
argv[2] == 456     (the second parameter as a string)

Why does **argv point to the first char and not the whole string?

char *argv[]  //an array of character pointers
*argv         // an array decays to a pointer, so this is functionally equivalent to
              // argv[0]
**argv        // Now the argv[0] decays to a pointer and this is functionally
              // equivalent to (argv[0])[0]

Likewise, why does *argv[0] point to the same thing as the previous example.

See above.

Why does *argv point to the whole first string, instead of the first char like the previous example?

See above.

Spear answered 13/11, 2012 at 15:34 Comment(4)
My Stack Overflow Rule of Thumb #3: "If the question starts with 'I understand', they don't."Succentor
@KerrekSB - That is a very wise rule. :)Spear
The last example holds true, because strings are actually arrays?Stencil
@Stencil - Exactly. argv is an array of "strings". *argv is the first of those "strings". **argv is the first character of the first "string". A "string" being a character array in C.Spear
D
0

This is all because an array is also a pointer to the first element in the array in c. **argv dereferences our pointer to pointer to char twice, giving us a char. *argv[0] is basically saying 'dereference that address, and return the first element in the array described by the address we just got from dereferencing,' which happens to be the same thing. *argv only dereferences once, so we still have a pointer to char, or a char array. *argv + 1 dereferences once, giving us the first character string, and then adds 1 to the address, giving us the address of the second element. Because pointers are also arrays, we can say that this is the array *argv minus the first element.

Dom answered 13/11, 2012 at 15:19 Comment(5)
An array is not a pointer. Arrays are arrays, and pointers are pointers.Succentor
Please revise. Arrays and pointers are different.Spear
I beg to differ. If you declare an array like this: int a[]; it is exactly the same as saying this: int* a;. Fundamentally, an array is just a pointer to a bunch of data arranged consecutively in memory.Dom
How do you figure? int a[]; won't compile since there's no array size. If you have int c = 3; you can assign it's address to a pointer such as int *b = &c;. If you try that with an array int a[1]; a = &c; you get error: incompatible types because you can't assign an array any address you want as you can a pointer. Because it's not a pointer. Arrays don't "point".Spear
Then try compiling this: int a[3] = {0,1,2}; int b = *a; int c = *(a+2); int* d = a; int e = d[1]; You will find it compiles just fine. I realize that you can't declare an array with no size, but if you declare one with a size, you can treat it as a pointer in every sense except assignment. This is because the array is fixed in memory location, and thus the array actually has a type of const int *. However, you can assign a random pointer to an array, and manipulate the array through the pointer just like the pointer is an array, because they are essentially the same thing.Dom

© 2022 - 2024 — McMap. All rights reserved.