C++ - char** argv vs. char* argv[]
Asked Answered
K

8

70

What is the difference between char** argv and char* argv[]? in int main(int argc, char** argv) and int main(int argc, char* argv[])?

Are they the same? Especially that the first part does not have [].

Karame answered 4/3, 2011 at 9:44 Comment(0)
S
72

They are entirely equivalent. char *argv[] must be read as array of pointers to char and an array argument is demoted to a pointer, so pointer to pointer to char, or char **.

This is the same in C.

Skewbald answered 4/3, 2011 at 9:46 Comment(5)
Thanks for your reply. Can you just explain this more: ...and an array argument is demoted to a pointer, so pointer to pointer to char, or char **.Karame
@user: It's a language rule. When you declare or define a function with the syntax X foo(Y a[]), then it actually becomes X foo(Y *a). What looks like an array argument to a function is really a pointer. Since argv is declared as an array (of pointers), it becomes a pointer (to pointers).Skewbald
The pointer to char part is clear for char** argv. But, where does the first pointer come from?Karame
@user: let's do a typedef char *String, so String is an alias for pointer to char. Now argv can be declared String argv[], which is an array of String. Since it's an argument, it's really a pointer to String, so String *argv. I hope this makes it clearer, I don't really follow your question.Skewbald
So... if I understand right, can we say... 1. We have a pointer to an array ( *argv which is the same as argv[]) 2. Within that array, we have several pointers to charactersLoraineloralee
T
51

They are indeed exactly the same.

The golden rule of arrays to remember is:

"The name of an array is a pointer to the first element of the array."

So if you declare the following:

char text[] = "A string of characters.";

Then the variable "text" is a pointer to the first char in the array of chars you've just declared. In other words, "text" is of type char *. When you access an element of an array using [index], what you're actually doing is adding an offset of index to the pointer to the first element of the array, and then dereferencing this new pointer. The following two lines will therefore initialize both variables to 't':

char thirdChar = text[3];
char thirdChar2 = *(text+3);

Using the square brackets is a convience provided by the language that makes the code much more readable. But the way this works is very important when you start thinking about more complex things, such as pointers to pointers. char** argv is the same as char* argv[] because in the second case "the name of the array is a pointer to the first element in the array".

From this you should also be able to see why it is that array indexes start from 0. The pointer to the first element is the array's variable name (golden rule again) plus an offset of... nothing!

I've had debates with a friend of mine as to which is better to use here. With the char* argv[] notation it may be clearer to the reader that this is in fact an "array of pointers to characters" as opposed to the char** argv notation which can be read as a "pointer to a pointer to a character". My opinion is that this latter notation doesn't convey as much information to the reader.

It's good to know that they're exactly the same, but for readablity I think that if the intention is an array of pointers then the char* argv[] notation conveys this much more clearly.

Tramroad answered 4/3, 2011 at 9:50 Comment(13)
Thanks for your reply. Can you just tell me how are the two forms the same?Karame
I've edited my answer which should give a more detailed explanation.Tramroad
"The golden rule of arrays to remember is" absolutely not what you wrote. This is a terrible, frustrating myth that just won't die. The name of an array can decay to a pointer to that array's first element. That is absolutely not the same as equivalence or identity.Micahmicawber
What's actually happening here is that the decay is implicit in function parameter lists.Micahmicawber
I've no idea what you're talking about. :STramroad
@JamesBedford: He is saying that the name of an array is NOT a pointer to the first element. It decays to such a pointer in certain contexts, such as function calls, which is what is happening here.Lunneta
It's a compile-time vs. run-time distinction? I found this helpful. https://mcmap.net/q/17745/-what-is-array-to-pointer-conversion-aka-decayTramroad
@JamesBedford: No, it's not really compile-time vs. run-time. An array like int x[10] is a stack variable consisting of 10 integers. (When its enclosing scope is put on the stack, its frame includes room for those 10 ints.) It is not a pointer, which would be a variable containing the address of something. There are no addresses stored on the stack when you make an array, as opposed to when you make a pointer. When you pass the array to a function, though, it decays; the function receives a pointer, not an array.Lunneta
Consider: when you take the address of an array, &x, you get the address of the data in the array. If x was a pointer, you would instead get the address of a memory location holding a pointer to the data, but you do not.Lunneta
&x is an address on the stack. Consider sizeof(x) and sizeof(&x).Department
@NickMatteo I have a question, if name of an array is NOT a pointer to its first element then how do we initialize a pointer to a character array without using '&' operator? e.g. char str[] = "random"; char *ptr = str; we don't do => char *ptr = &str; It means 'str' (name of an array) contains an address i.e. 'str' IS a pointer to its first element. Here we are not decaying the array to a pointer since we haven't passed the array in a function call still 'str' is behaving like a pointer. Why?Petepetechia
@LightnessRacesinOrbit I have the same question for you that I asked Nick above.Petepetechia
@Petepetechia That's also an array variable decaying to a pointer. It can happen in pretty much any expression: someone in the question James linked quoted this part of the standard: 'An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.”'Lunneta
L
5

For the first part of the question:

  • char** argv: pointer to a pointer to a char
  • char* argv[]: pointer to an array

So the question is whether a pointer to a type C and an array C[] are the same things. They are not at all in general, BUT they are equivalent when used in signatures.

In other words, there is no difference in your example, but it is important to keep in mind the difference between pointer and array otherwise.

Lowis answered 4/3, 2011 at 9:49 Comment(6)
The link is to the C FAQ, but the question is tagged C++.Skewbald
C++ is an extension of C... So everything that applies to C is relevant to C++.Tramroad
Nope. There are thinks illegal in C and allowed in C++, so when some source says "it is illegal to..." doesn't mean this applies to C++.Kalasky
C and C++ are quite similar on this aspect, though.Lowis
"char* argv[]: pointer to an array" No. T* param[] is rewritten to T** param at the syntax level. You could pass a T** into that function that had nothing whatsoever to do with an array.Micahmicawber
You always want to read pointer types from right to left. Hence, char *argv[] reads as follows: array of pointers to characters, and not "pointer to an array".Clotildecloture
R
4

For all practical purposes, they're the same. This is due to C/C++'s handling of arrays passed as arguments, where an array decays to a pointer.

Railey answered 4/3, 2011 at 9:47 Comment(0)
U
3

The bracket form is only useful in statement declarations like:

char *a[] = {"foo", "bar", "baz"};
printf("%d\n", sizeof a / sizeof *a);
// prints 3

Because it knows at compile time the size of the array. When you pass a bracket form as parameter to a function (main or some other one), the compiler has no idea what the size of the array would be at runtime, so it is exactly the same as char **a. I prefer char **argv since it's clearer that sizeof wouldn't work like it would on the statement declaration form.

Unswear answered 29/10, 2013 at 15:59 Comment(0)
V
-1

There is a difference between TYPE * NAME and TYPE NAME[] in both C and C++. In C++ both types are not interchagneable. For example following function is illegal (you will get an error) in C++, but legal in C (you will get warning):

int some (int *a[3]) // a is array of dimension 3 of pointers to int
{
    return sizeof a;
}

int main ()
{
    int x[3][3];
    std::cout << some(x)<< std::endl;
    return 0;
}

To make it legal just change signature to int some (int (*a)[3]) (pointer to array of 3 ints) or int some (int a[][3]). The number in last square brackets must be equal to an argument's. Converting from array of arrays to an array of pointers is illegal. Converting from pointer to pointer to array of arrays is illegal too. But converting pointer to pointer to an array of pointers is legal!

So remember: Only nearest to dereference type signature doesn't matter, others do (in the context of pointers and arrays, sure).

Consider we have a as pointer to pointer to int:

int ** a;
&a     ->     a    ->    *a    ->    **a
(1)          (2)         (3)          (4)
  1. You cannot change this value, the type is int ***. May be taken by function as int **b[] or int ***b. The best is int *** const b.
  2. The type is int **. May be taken by function as int *b[] or int ** b. Brackets of the array declaratin may be leaved empty or contain any number.
  3. The type is int *. May be taken by function as int b[] or int * b or even void * b
  4. Should be taken as int parameter. I don't want to fall into details, like implicit constructor call.

Answering your question: the real type of argumets in main function is char ** argv, so it may be easily represented as char *argv[] (but not as char (*argv)[]). Also argv name of main function may be safely changed. You may check it easily: std::cout << typeid(argv).name(); (PPc = pointer to p. to char)

By the way: there is a cool feature, passing arrays as references:

void somef(int (&arr)[3])
{
    printf("%i", (sizeof arr)/(sizeof(int))); // will print 3!
}

Moreover pointer to anything may be implicitly accepted (converted) by function as void pointer. But only single pointer (not pointer to pointer etc.).

Further reading:

  1. Bjarne Stroustrup, C++, chapter 7.4
  2. C pointers FAQ
Volcanology answered 6/5, 2016 at 15:33 Comment(4)
"There is a difference between TYPE * NAME and TYPE NAME[] in both C and C++." Nonsense. "In C++ both types are not interchagneable." No, only the first type actually exists. "For example following function is illegal (you will get an error)" That's because int* ar[3] and int* ar[] are completely different things. The misinformation goes on and on...Micahmicawber
What does clothest mean??Mixie
@Dan i have meant nearest, sorryVolcanology
@Volcanology You can edit your answer to correct it if you like.Mixie
S
-1

This is a simple example I came up with, which have two functions (Main_1, Main_2) take the same arguments as the main function.

I hope this clear things up..

#include <iostream>

void Main_1(int argc, char **argv)
{
    for (int i = 0; i < argc; i++)
    {
        std::cout << *(argv + i) << std::endl;
    }
}

void Main_2(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++)
    {
        std::cout << *(argv + i) << std::endl;
    }
}

int main()
{

    // character arrays with null terminators (0 or '\o')
    char arg1[] = {'h', 'e', 'l', 'l', 'o', 0};
    char arg2[] = {'h', 'o', 'w', 0};
    char arg3[] = {'a', 'r', 'e', '\0'};
    char arg4[] = {'y', 'o', 'u', '\n', '\0'};

    // arguments count
    int argc = 4;

    // array of char pointers (point to each character array (arg1, arg2, arg3 and arg4)
    char *argPtrs[] = {arg1, arg2, arg3, arg4};

    // pointer to char pointer array (argPtrs)
    char **argv = argPtrs;

    Main_1(argc, argv);
    Main_2(argc, argv);

    // or

    Main_1(argc, argPtrs);
    Main_2(argc, argPtrs);

    return 0;
}

Output :

hello
how
are
you

hello
how
are
you

hello
how
are
you

hello
how
are
you
Saltpeter answered 16/11, 2021 at 14:1 Comment(0)
A
-2

Both are same for your usage except for the following subtle differences:

  • Sizeof will give different results for both
  • Also second one may not be reassigned to new memory area since it's an array
  • With second one you can use only those indexes which are valid. It's unspecified by C/C++ if you try to use an array index beyond array length. However with char** you can use any index from 0 to ...
  • Second form can only be used as formal parameters to a function. While first can even be used to declare variables within a stack.
Audiometer answered 4/3, 2011 at 10:4 Comment(1)
What? In the context the OP is asking in, every one of these is incorerct.Le

© 2022 - 2024 — McMap. All rights reserved.