Strictly speaking, there are a number of properties that must be present for argv
to be an array. Let us consider some of those:
¹/ There can be no array pointed at by a null pointer, as null pointer are guaranteed to be an address distinct from that of any object. Therefore, argv
in the following code can't be an array:
#include <assert.h>
int main(int argc, char *argv[]) {
if (argv) return main(0, 0);
assert(argv == 0); // argv is a null pointer, not to be dereferenced
}
²/ It's invalid to assign to an array. For example, char *argv[] = { 0 }; argv++;
is a constraint violation, but int main(int argc, char *argv[]) { argv++; }
compiles and runs fine. Thus we must conclude from this point that argv
is not an array when declared as an argument, and is instead a pointer that (might) point into an array. (This is actually the same as point 1, but coming from a different angle, as calling main
with a null pointer as argv
is actually reassigning argv
, something we can't do to arrays).
³/ ... As the C standard says:
Another use of the sizeof operator is to compute the number of elements in an array:
sizeof array / sizeof array[0]
For example:
#include <assert.h>
int main(int argc, char *argv[]) {
size_t size = argc+1; // including the NULL
char *control[size];
assert(sizeof control / sizeof *control == size); // this test passes, because control is actually an array
assert(sizeof argv / sizeof *argv == size); // this test fails for all values of size != 1, indicating that argv isn't an array
}
⁴/ The unary &
address-of operator is defined such that when applied to an array, will yield the same value of a different type, so, for example:
#include <assert.h>
int main(int argc, char *argv[]) {
char *control[42];
assert((void *) control == (void *) &control); // this test passes, because control is actually an array
assert((void *) argv == (void *) &argv); // this test fails, indicating that argv isn't an array
}