Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I don't understand this

    main(argc, argv)
    int argv[]; {
Is that still valid today?


I remember being absolutely thrilled when function prototypes were introduced with the 'proposed' ANSI C standard (X3J11). The first implementation I got my hands on was Borland's Turbo C circa 1988.

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'.


It's a holdover from Fortran (and probably from before that). Whereas modern day we would define functions as

    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[]".


It's called a K&R style function definition, which was the way to do it back in the day. Basically, you define your parameter names first, then you define the parameter types immediately after the function but before the opening curly brace. It's definitely not recommended today and can result in undefined behavior if your compiler doesn't recognize it. If you're working with legacy code, though, I'm pretty sure you can set some C compilers to allow for it.

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.


The 1999 ISO C standard dropped the "implicit int" rule, so this:

    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).


We're talking about the code from the actual source files, not the standard.

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.


Yes it is. Originally that's how you specified parameters: just the name in the parentheses, then (optionally) the types in a format similar to variable declarations before the function body. If you didn't specify a type it would default to int, so argc above would be an int.

All modern C compilers still accept this style for backwards compatibility. I'm not sure about C++ compilers.


Function declaration for main. The type declarations for the function parameters default to int, but can be specified outside of the parens before the curly brace.

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.


I don't know if it's still valid by the spec, but it's still supported by some compilers. There was a post on vim reaching 7.3.0.1000 the other day, and that's still using that style of typing.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: