问题
I am confused as to how the following passage matches up with the code that follows it:
Since argv is a pointer to an array of pointers, we can manipulate the pointer rather than index the array. This next variant is based on incrementing argv, which is a pointer to pointer to char, while argc is counted down:
#include <stdio.h>
/* echo command-line arguments; 2nd version */
main(int argc, char *argv[])
{
while (--argc > 0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");
return 0;
}
Isn't char *argv[]
just an array of pointers? Wouldn't a pointer to an array of pointers be written as char *(*argv[])
or something similar?
As a side note, is it normal that in general I find declarations that mix arrays and pointers rather confusing?
回答1:
Such terms as "pointer to array" or "to point to an array" are often treated rather loosely in C terminology. They can mean at least two different things.
In the most strict and pedantic sense of the term, a "pointer to array" has to be declared with "pointer to array" type, as in
int a[10];
int (*p)[10] = &a;
In the above example p
is declared as a pointer to array of 10 int
s and it is actually initialized to point to such an array.
However, the term is also often used is its less formal meaning. In this example
int a[10];
int *p = &a;
p
is declared as a mere pointer to int
. It is initialized to point to the first element of array a
. You can often hear and see people say that p
in this case also "points to an array" of int
s, even though this situation is semantically different from previous one. "Points to an array" in this case means "provides access to elements of an array through pointer arithmetic", as in p[5]
or *(p + 3)
.
This is exactly what is meant by the phrase "...argv
is a pointer to an array of pointers..." you quoted. argv
's declaration in parameter list of main
is equivalent to char **argv
, meaning that argv
is actually a pointer to a char *
pointer. But since it physically points to the first element of some array of char *
pointers (maintained by the calling code), it is correct to say semi-informally that argv
points to an array of pointers.
That's exactly what is meant by the text you quoted.
回答2:
Where C functions claim to accept arrays, strictly they accept pointers instead. The language does not distinguish between void fn(int *foo) {}
and void fn(int foo[])
. It doesn't even care if you have void fn(int foo[100])
and then pass that an array of int [10]
.
int main(int argc, char *argv[])
is the same as
int main(int argc, char **argv)
Consequently, argv
points to the first element of an array of char
pointers, but it is not itself an array type and it does not (formally) point to a whole array. But we know that array is there, and we can index into it to get the other elements.
In more complex cases, like accepting multi-dimensional arrays, it is only the first []
which drops back to a pointer (and which can be left unsized). The others remain as part of the type that is being pointed to, and they have an influence on pointer arithmetic.
回答3:
The array-pointer equivalence thing only holds true only for function arguments, so while void fn(const char* argv[])
and void fn(const char** argv)
are equivalent, it doesn't hold true when it comes to the variables you might want to pass TO the function.
Consider
void fn(const char** argv)
{
...
}
int main(int argc, const char* argv[])
{
fn(argv); // acceptable.
const char* meats[] = { "Chicken", "Cow", "Pizza" };
// "meats" is an array of const char* pointers, just like argv, so
fn(meats); // acceptable.
const char** meatPtr = meats;
fn(meatPtr); // because the previous call actually cast to this,.
// an array of character arrays.
const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
fn(vegetables); // does not compile.
return 0;
}
"vegetables" is not a pointer to a pointer, it points directly to the first character in a 3*10 contiguous character sequence. Replace fn(vegetables) in the above to get
int main(int argc, const char* argv[])
{
// an array of character arrays.
const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
printf("*vegetables = %c\n", *(const char*)vegetables);
return 0;
}
and the output is "A": vegetables itself is pointing directly - without indirection - to the characters, and not intermediate pointers.
The vegetables assignment is basically a shortcut for this:
const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0";
vegetables = __vegetablesPtr;
and
const char* roni = vegetables[2];
translates to
const char* roni = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);
回答4:
Since argv is a pointer to an array of pointers
.
This is wrong. argv
is an array of pointers.
回答5:
Since argv is a pointer to an array of pointers,
No, not even close.
Isn't
char *argv[]
just an array of pointers?
No, it's a pointer to pointers.
回答6:
"Pointer to the first element of an array" is a common construct. Every string function uses it, including stdio functions that input and output strings. main uses it for argv.
"Pointer to an array" is a rare construct. I can't find any uses of it in the C standard library or POSIX. grepping all the headers I have installed locally (for '([^)]*\*[^)]) *\['
) I find exactly 2 legitimate instances of pointer-to-array, one in libjpeg and one in gtk. (Both are struct members, not function parameters, but that's beside the point.)
So if we stick to official language, we have a rare thing with a short name and a similar but much more common thing with a long name. That's the opposite of the way human language naturally wants to work, so there's tension, which gets resolved in all but the most formal situations by using the short name "incorrectly".
The reason we don't just say "pointer to pointer" is that there's another common use of pointers as function parameters, in which the parameter points to a single object that's not a member of an array. For example, in
long strtol(const char *nptr, char **endptr, int base);
endptr
is exactly the same type as argv
is in main
, both are pointer-to-pointer, but they're used in different ways. argv
points to the first char *
in an array of char *
s; inside main you're expected to use it with indexes like argv[0]
, argv[optind]
, etc., or step through the array by incrementing it with ++argv
.
endptr
points to a single char *
. Inside strtol
, it is not useful to increment endptr
or to refer to endptr[n]
for any value of n
other than zero.
That's semantic difference is expressed by the informal usage of "argv is a pointer to an array". The possible confusion with what "pointer to array" means in formal language is ignored, because the natural instinct to use concise language is stronger than the desire to adhere to a formal definition that tells you not to use the most obvious simple phrase because it's reserved for a situation that will almost never happen.
来源:https://stackoverflow.com/questions/17254853/argv-pointer-to-an-array-of-pointers