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
}
Does this mean that the command line strings are stored in memory as an array of pointers to array of char?
Yes. IMHO the whole confusion is caused byThe program stores the command line strings in memory ...
; the point is that all this happens before main() is called. Main() is just a function, which is called with two arguments: an int and a pointer to an array of string pointers. – Shererargv
behaves as specified in the C Standard – Scleroticargv
array is in memory at process startup, in a format suitable for passing by reference tomain
. So thecrt0
libc startup code doesn't have to do anything with argv except pass a pointer to it tomain()
. In Linux, the kernel puts argv and the environment block at the top of the user-space stack. The x86 flavours of the System V ABI are online here. – Zeiler