main(argc, argv)
int argv[]; {
Is that still valid today? main(argc, argv)
int argv[]; {
Is that still valid today?The standard now known as C89/C90 had been in committee for many years before being 'released'. This didn't stop the tool vendors (like Borland) from supporting the 'proposed' standard much earlier than 1989.
Unfortunately, commercial UNIX vendors (like HP, in my case), were very slow to adopt the standard and update the cc compiler in their distribution. This forced us to work in K&R for a good time after 1990, all the while grumbling that $150 MS-DOS compilers already 'had ANSI'.
int foo(int i, int j) {...}
in Fortran you would do (! is comment) function foo(i, j)
integer :: foo !return type
integer :: i
integer :: j
!body goes here
Early C stuck to that style, so you would just put the names of the variables in the declaration, and then before the body give them types. The reason only argv is mentioned in that example is that C assumes a variable is an int if not declared otherwise, so there's no reason to put "int argc" like "int argv[]".To explain further:
main(argc, argv)
int argv[]; {
is equivalent to: int main(int argc, int argv[]) {
The old style definition works because C had a default type of int, so the type specifications for the function main and the parameter argc could be omitted.As for int argv[]? What that actually represents is an array of memory addresses that hold the command line arguments given. Obviously this becomes a problem if you're on a 64-bit system, where int and (void * ) are two different sizes. However, I checked this out on my 64-bit machine and it works just fine:
int main(int argc, unsigned long long argv[]) {
char *firstarg = (void *)(argv[1]);
printf("%s", firstarg);
}
which, given "./a.out pickles" will print "pickles" (argv[0] gives the memory address of the cstring "./a.out"). I'm guessing that, in the case of a compiler, the memory addresses of arguments are more relevant to have than the arguments themselves. main(argc, argv)
char *argv[];
{
/* ... */
}
is illegal (strictly speaking, it's a "constraint violation"). Note that it's char *argv[]
not int argv[]
But this: int main(argc, argv)
int argc;
char *argv[];
{
/* ... */
}
is still perfectly valid.As for this:
int main(int argc, unsigned long long argv[]) {
char *firstarg = (void *)(argv[1]);
printf("%s", firstarg);
}
it's not a constraint violation, but its behavior is undefined (unless your compiler specifically supports and documents that particular form as an extension).Look here (lines 22 and 23): https://github.com/mortdeus/legacy-cc/blob/master/prestruct/...
The compiler code states int argv[], not char argv[] (I assumed this is why the OP asked for clarification in the first place, since char argv[] is much more common).
You're right, in theory this is undefined behavior, but in practice on a 32-bit system, sizeof(int) will almost always be equal to sizeof(void *). I was just demonstrating how one could recreate the code in the compiler while on a 64-bit architecture.
All modern C compilers still accept this style for backwards compatibility. I'm not sure about C++ compilers.
The int argv[]; is where the declaration for argv is happening and is being declared as an pointer for ints.
You can also see elsewhere in the code where they are passing pointer addresses (as int params) into functions and then using the address to build pointers referencing that data.