Yes, it helps.
Let's go in depth, with this statement:
We have now have declared a one-dimensional array of pointer to char with two elements*.
You mean by this, that the pointer points to an array of char? And these elements can have an arbitrary space between them as sokoloff pointed out.
But crucial here is if it points to an array of char in memory. So where an array starts in memory, there should point the pointer.
Let's take the declaration again
char *a[] = { "Fir", "Sec" };
- We are declaring an object named a: a
- Which is an array: a[]
- Of pointers: *a[]
- To char: char *a[]
- Its size is two elements, as, though not directly specified, it is set by the initializer.
Now, this is almost exactly as the "argv" data structure is defined (nitpicking details below^).
When the name a is used in any expression (i.e. more or less any place that is not the declaration itself or the single parenthesized argument of a sizeof operator) the general rule applies: it will decay to a pointer to its first element, giving, in fact, a char **.
(And this is where the second rule would apply: a function parameter, if declared as char *arg[], is adjusted to yield a pointer, so it's equivalent to char **arg.)
This pointer ( "a" ) does not point to an array of char, but to an array of pointers to char (the elements of a).
Each element, in turn, is pointing to an array of char in memory, allocated where the compiler sees fit^.
This realizes exactly what my original picture tried to show: an array of char*, and the relative pointed to array of chars.
You might say, simplifying, that in C arrays are how stuff is stored in memory, but actual access to them is always realized through pointers, possibly with the help of the [] operator.
Pointers "know" the size of the type they are pointing to, so pointer arithmetic and [] works as expected.
Pointers do not know how many elements they are pointing to (0 is a corner case, 1 or more cover the case of a single variables and arrays).
A null pointer to any type has the value 0 and does not point to anything.
^Nitpicks: argv (or whatever you might want to call it, the name is just conventional) will always contain also a final null pointer element. Furthermore, the char arrays that represent string literals ("Fir", "Sec") are not modifiable (6.4.5, clause 6), while the ones pointed by argv elements can be modified (5.1.2.2.1, end of clause 2).