Thread: Strange main function

  1. #1
    Registered User
    Join Date
    Feb 2003
    Posts
    596

    Strange main function

    This main() does not conform to anything I can find in K&R, or any textbook, or C draft standard, but GCC compiles it with no complaints & it runs without errors. (Call it with no arguments.)
    Code:
    #include <stdio.h>
    
    main(x,y,z) 
    {
      printf("Hello world!\n");
      printf("x = %d, y = %d, z = %d\n",x,y,z);
      y = 2*x;
      if(x == 5)
        return;
      else 
        main( x+1,y,100 );
    }
    I also find that I can specify other types for the parameters, as in
    Code:
    main(x,y,z) double y; char z;
    {
    //...
    }
    or add any number and type of additional parameters -- in fact, apparently to do anything with main that I can with any other function, plus it seems that the parameters are ints by default, with no need to specify a type unless I want some non-numeric type.

    Why does this work? Is this just a nonstandard "feature" to let us have fun obfuscating code, or is there a better explanation?

  2. #2
    and the hat of copycat stevesmithx's Avatar
    Join Date
    Sep 2007
    Posts
    587
    This is strange indeed.
    But if i compile it with -Wmain option of gcc i get 3 warnings.
    Edit:
    Actually i compiled it like this:

    Code:
    gcc -W -pedantic -Wmain prg_name.c
    Last edited by stevesmithx; 05-31-2009 at 09:47 PM.
    Not everything that can be counted counts, and not everything that counts can be counted
    - Albert Einstein.


    No programming language is perfect. There is not even a single best language; there are only languages well suited or perhaps poorly suited for particular purposes.
    - Herbert Mayer

  3. #3
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Why does this work? Is this just a nonstandard "feature" to let us have fun obfuscating code, or is there a better explanation?
    It's standard, and it's not for fun. That's the way C used to work back in the bad old days (i.e., before 1989).

    Well, actually, your definition of main() is nonstandard (too many parameters), but that sort of function declaration is standard. It's called K&R-style, and it should absolutely not be used in modern code. K&R-style declarations are not prototypes, and thus there is no type checking. That is a bad thing. The only reason the standard still allows K&R declarations is for backward compatibility.

    The reason you can leave the types off is the same reason you don't have to declare main() as returning int. Before C99 there was another C misfeature, and that's implicit int. In a lot of places, if you don't specify a type, int is assumed. A K&R declaration is one of those places.
    Code:
    this_is_bad(x, y, z)
    char *y;
    double z;
    {
    }
    void this_is_good(int x, char *y, double z)
    {
    }
    int main(void)
    {
      /* diagnostics not required */
      this_is_bad();
      this_is_bad(10, 20, 30);
    
      /* diagnostics required */
      this_is_good();
      this_is_good(10, 20, 30);
    
      return 0;
    }
    For the record, when I tested the above:
    • gcc complains only about calls to this_is_good()
    • sunstudio is the same as gcc
    • clang complains about the first call to this_is_bad() and both calls to this_is_good()
    • icc complains about all four calls

    Each of the compilers is conforming, in that the required diagnostics are performed. They also variously complain about things with the K&R declaration, which is nice. Even if the code complies with the standard, it is terrible, and compilers are right to make noise.

  4. #4
    Making mistakes
    Join Date
    Dec 2008
    Posts
    476
    I thought you could also declare main as "int main(int argc, char **argv, char **envp)". Or is that only valid any extension standard, such as in POSIX?

  5. #5
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    cas: Thanks for the explanation. Compiling my code with GCC 4.2.4. with option -W only gives these warnings:
    Code:
     gcc -W xyz.c
    xyz.c: In function ‘main’:
    xyz.c:4: warning: type of ‘x’ defaults to ‘int’
    xyz.c:4: warning: type of ‘y’ defaults to ‘int’
    xyz.c:4: warning: type of ‘z’ defaults to ‘int’
    and compiling with -Wall only warns about the return:
    Code:
     gcc -Wall xyz.c
    xyz.c:4: warning: return type defaults to ‘int’
    xyz.c: In function ‘main’:
    xyz.c:9: warning: ‘return’ with no value, in function returning non-void
    I finally found some info about "old style" function declarations and calls in Appendix A7.3.2 and A8.6.3 of my K&R second edition , but nothing specific about main. It seems that there is nothing built into the compiler that restricts the parameter list of main to be only (void) or (int, char**). Is there anything actually written in the standard that requires main to only those two parameter lists?

    Obviously I agree that this is horrible programming style. I only stumbled across it while trying to understand some intentionally obfuscated code.


    Brafil: apparently you can declare main to be anything you want. It may be nonstandard, but the compiler doesn't seem to care. Can you compile this in MSVC?
    Code:
    #include <stdio.h>
    
    main(x,y,z,a,b) double y; char z; char *a; double b;  // x will default to int
    {
      if( x == 1 )
        printf("Hello world\n");
      else
        printf("x = %d, y = %f, z = %c, a = %s, b = %f\n",x,y,z,a,b);
      y = 2*x;
      if(x == 5)
        return;
      else 
        main( x+1,y,'c',"hello",3.3*x );

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The standard requires a compiler to understand those two forms of main, and any other forms it feels could be handy (as mentioned above there is a third available in POSIX) (just as a compiler is allowed to do other extensions as long as it compiles "strictly conforming" code correctly).

  7. #7
    Making mistakes
    Join Date
    Dec 2008
    Posts
    476
    Dunno. I would have to restart (using linux at the moment). It's not standard to me. At least, you can call main from main. return without a value is not allowed, neither is 5 parameters to main. y is a double, b too. etc etc.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Because no types are listed, when the compiler sees:

    Code:
    main(a, b, c)
    It thinks

    Code:
    int main(int a, int b, int c)
    This is okay, because the true prototype of main() is

    Code:
    int main(int, char **, char **)
    Apparently, the conversion from char ** to int is silent here, and you see no warning, at least at the default level.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    In what sense is the "true prototype" of main
    Code:
    int main(int, char  **, char **)
    ?

    According to my compiler warnings, not only does the return type default to int, but if I don't declare any argument types, all of the arguments default to int.

  10. #10
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by R.Stiltskin View Post
    According to my compiler warnings, not only does the return type default to int, but if I don't declare any argument types, all of the arguments default to int.
    Yes, that is true of every function, not just main. *Unless* you define a function with no parameters like this:
    Code:
    int somefunc(void) {
    You can submit a series of ints to the function and the compiler won't say boo, eg:
    Code:
    int somefunc ();
    int x, y, z;
    somefunc(x,y,z);
    Is legal (but pointless).

    So, the true prototype of main is "int main (int, char**, char**)" in the sense that this is true...or at least, why wouldn't it be?
    Last edited by MK27; 06-01-2009 at 10:15 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  11. #11
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Would it be more accurate to say that the conventional prototype of main is "int main (int, char**)" and the standard prototype of main is "int main (int, char**, char**)"?

    Yes, your example is legal but pointless, but my examples, posted earlier, may be non-standard, confusing, ugly, evil, etc., but not pointless, in that they actually do something.

  12. #12
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The C standard specifies int main(void) or int main(int, char**) (or moral equivalents). POSIX also specifies int main(int, char**, char**), but not everybody cares about POSIX.

  13. #13
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Would it be more accurate to say that the conventional prototype of main is "int main (int, char**)" and the standard prototype of main is "int main (int, char**, char**)"?
    No. The two standard forms of main are: "int main(void)" and "int main(int, char **)". Implementations may accept others, but they would be non-standard. Unix-like systems accept a third parameter, in the form of an environment pointer, but it is not standard C.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by R.Stiltskin
    Would it be more accurate to say that the conventional prototype of main is "int main (int, char**)" and the standard prototype of main is "int main (int, char**, char**)"?
    It would be more accurate that these are standard:
    Code:
    int main(void)
    
    int main(int argc, char *argv[])
    and this is permitted and may be standard with respect to additional standards:
    Code:
    int main(int argc, char *argv[], char *envp[])
    However, by "true prototype" brewbuck probably means to say that the arguments that will be passed would conform to the types listed in the signature cited. As such, you could write:
    Code:
    #include <stdio.h>
    
    int main(argc, argv)
    {
        printf("%d %s\n", argc, ((char**)argv)[0]);
        return 0;
    }
    and the number of command line arguments, as well as the name of the program (or an empty string) would be printed when the program is run.

    EDIT:
    Ah, but note that an assumption here is that the size of a pointer is no greater than the size of an int.
    Last edited by laserlight; 06-01-2009 at 10:51 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Thanks, I finally found the full discussion of main in the "program startup" section of the draft standard (I don't have access to the actual current standard).

    I guess GCC and other compilers allow alternate forms of main in order to maintain compatibility with old (pre-ANSI-C) code? The (draft) standard doesn't seem to be concerned at all with backward compatibility.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 05-13-2011, 08:28 AM
  2. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  3. Game Pointer Trouble?
    By Drahcir in forum C Programming
    Replies: 8
    Last Post: 02-04-2006, 02:53 AM
  4. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  5. Struct *** initialization
    By Saravanan in forum C Programming
    Replies: 20
    Last Post: 10-09-2003, 12:04 PM