Some main facts

Introduction

Every C or C++ programmer knows about the main function. It’s probably the first function every programmer sees. Usually, main is introduced in the following form:

int main(int argc,char *argv[])
{
}

The main function is the application’s “designated start”, meaning that’s basically where the program starts its execution. Apart from that, it’s a very old function and has some very special properties, which I’m going to address here.

Random facts

Simple facts

First of all, main is not necessarily the first function that’s executed in your code. Consider the following simple example:

#include <iostream>
// Include ostream for operator<, since we're so pedantic 🙂
#include <ostream>

namespace
{
class foo
{
public:
  foo()
  {
    std::cout << "Mother of god, where's main?\n"
  }
};

foo f;
}

int main(int argc,char *argv[])
{
  std::cout << "I'm here!\n";
}

This will output:

Mother of god, where's main?
I'm here!

This must happen since global objects have to be initialized before main starts (as a side note, the name argv stands for “argument vector”, and “vector” stands for “array”).

Additionally, you don’t have to have a main function in your program. If you’re designing a library, there’s no need for a starting point. If you’re compiling and linking an executable, however, you’ll get linker errors if main is missing.

What we definitely know

First of all, what do the parameters to main really mean? If you know C/C++ long enough, you might have developed a certain hesitation when it comes to reality vs. formal definitions. For example, one might expect float and double values to be encoded in the standard IEEE 754 floating point encoding, and that’s certainly true for most systems. But the standard will tell you nothing of the likes. Instead, floating point values are completely implementation defined. They could be stored as random bit sequences, the Standard doesn’t care, it’s still valid.

Fortunately, when it comes to main, everything’s pretty much as you expect it to be: The argc parameter denotes the number of arguments passed to the program and argv denotes the parameters themselves. It was a little surprising to me that even argv[0] is well-defined. It’s either empty or it’s the name used to invoke the program. Of course, even if argv[0] is not empty, it’s not a name you can rely on. For example, it doesn’t necessarily contain an absolute path to the executable or even the name of the executable file on disk.

The following code is also correct:

int main(int argc,char *argv[])
{
  while(argv)
  {
    char *current_parameter = *argv;
    std::cout << current_parameter << "\n";
    argv++;
  }
}

The program will print the command line argument it’s given. This works because it is guaranteed that argv[argc] is NULL.

main‘s return code is, mostly implementation defined. There are two values that designate a “successful” program termination: 0 and EXIT_SUCCESS. The latter is defined in cstdlib (or stdlib.h if you’re a C guy). There’s one return value that designates nonsuccessful termination: EXIT_FAILURE (also defined in cstdlib or stdlib.h).

Last but not least, there are two main prototypes that every implementation must accept:

int main();
int main(int,char*[]);

There might be other correct prototypes of main (meaning they’re called main and return an int, but they take other parameters), but they’re not portable. main cannot be overloaded, so you have to define exactly one main function in your program.

Note that technically, the following declarations are also correct:

main();
main(int,char*[]);
main(void);

Simply because in C, a function without a return type returns an int by default. This is ill-formed in C++, however. The notation main(void); is a shorthand for “takes no parameters” in C (originally, main() meant the same as main(...), so “take arbitrarily many parameters”).

Historical oddities

As I said, the main is a very old concept. It has been in C since its inception. Because of that and because of backwards compatibility, main admits some idiosyncracies. For example: Why is the argc parameter a signed integer? Can it be negative? Are there even valid use cases where this makes sense? The answer is no. The standard says it cannot be negative. It is an int, however, for historical reasons. You see, main was there before there even was unsigned. It’s like going back to prehistoric times!

Another thing is the argv parameter. Since main is C, its type is char*[] (or char**) and not a nice std::vector<std::string> which we’d all appreciate much more. However, a little stranger to me is the fact that it’s not a constant pointer. Again you may ask yourselves, is there a use case where we might change the parameters passed to the program? Is that even defined? Again, the answer is no. The pointer isn’t const because main simply is older than const.

It’s a lack of the holy const-correctness, and that might even bother you for a non-ideological reason: you cannot convert the argv pointer to a const char ** without a const_cast! And, evil as I am, I leave it up to you to figure out why. Anyhow, that’s why the main.m files of most of the Cocoa Objective-C programs look like this:

int main(int argc, char *argv[])
{
  return NSApplicationMain(argc, (const char **) argv);
}

main and return are also a strange couple. In some really bad programming books, you stumble upon the following main definition:

void main()
{
}

This is wrong but it might compile on some lenient compilers. What drives people to write it that way, you might ask. I think one of the reason is that the following program is valid C/C++:

int main()
{
}

Y U NO RETURN ANYTHING?!

You might think that this is undefined behavior, and it is! … in general. With main however, it’s fine. It’s equivalent to:

int main()
{
  return 0;
}

Another historical oddity.

Other mains

As I said above, you don’t really need a main. Libraries, for example, are main-less almost by definition. But there’s another unfortunate use case for not having a standard main as described above, and that’s when you’re building a Windows application.

On Windows, you have two choices when creating an application (actually, when linking an application). You can create either a console application or a graphical application. You might think that the difference lies in what you can do with your program. Do fancy GUI stuff versus boring console text output. But that’s not really true. You can create a console application that creates windows as usual. In fact, a console application creates at least one window: A terminal window showing the output of stdout and stderr. For developers, a handy tool. For end users, “watching shit scroll down their screen” is usually not very healthy. To supress the console window, create a graphical application.

And here’s the catch: A graphical application doesn’t have a main as defined above. It’s not even a non-portable main taking parameters other than the familiar (argc,argv) pair. It’s a whole new beast, and its name is WinMain. If you haven’t got one in your graphical application, you’ll get a linker error.

Its prototype looks intimidating:

#include <windows.h>

int CALLBACK WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,
  int nCmdShow
);

A lot of cryptic macros in there, not really self-explanatory. At least it returns an integer! I’ll not explain it in intimiate detail, just the main points: the first two parameters, hInstance and hPrevInstance, are not that important. If you need hInstance, you can get it from a function instead of having them passed in here. hPrevInstance is always NULL.

The next parameter is the equivalent of argc and argv. Let’s read it together: LPSTR: A long pointer to a string. Just one string, you ask? Yes, it’s not neatly packaged into parts, it’s one string. It’s not even a UTF-16 string (everything else is on Windows).

The last parameter is a hint to the application stating how it is to be shown when it starts. This parameter might indicate that the application starts minimized, for example. I know of no way to get to this value using a function, so you might want to consider passing this along to your window creation subsystem.

The biggest annoyance with this, however, is the very first line of the code above: windows.h. This header is so extremely crappy, I have almost no words for it. It’s full of macros that have very common names. For example, it defines min and max macros, so code using these words will break (for example, std::min(a,b)). It also defines near, so your near viewing planes might get replaced by random crap.

Isolating WinMain

So my advice: Isolate the code that uses windows.h very cleanly from the rest (if you’re not going to use the WinAPI, that is). I’ll give a few hints on how to do that effectively.

There should be one file designated just for the main function. This file should use main on POSIX systems like Linux and Mac OSX, and WinMain on Windows. We want to forward the main function to our own application starting function and pass along all the information we get in main. On POSIX systems, that’s just argc,argv and on Windows, we have the nShowCmd parameter. What we get is the following:

#ifdef _WINDOWS
#include <windows.h>
#include <stdlib.h>
#endif

int the_real_main(int,int,int);

#ifdef _WINDOWS
int CALLBACK WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,
  int nCmdShow
)
{
  int argc = __argc;
  char *argv[] = __argv;
#else
int main(int argc,char *argv[])
{
  int nCmdShow = -1;
#endif
  return the_real_main(argc,argv,nCmdShow);
}

As you can see, we get our argc,argv back from the Windows runtime using the internal variables __argc,__argv which are defined in stdlib.h. Also, on POSIX systems, we (randomly) assign -1 to nCmdShow. The window system (whatever you use) should only access that variable when we’re on Windows, in which case it’s the parameter you receive in WinMain. Now, somewhere in your program, you have your the_real_main function that’s almost completely agnostic of what operating system we’re on.

Almost, you say?

Well, yes. We still need some special behavior for Windows (not just the nCmdShow stuff). On Windows, we cannot just return from main as usual. We have to signal that we’re quitting the application with an exit code and then wait for the quit message, containing the real exit code. Oh dear. Also we might get the quit message before the program is done. Of course, in that case, we also return the message’s exit code parameter.

To signal our application exit, we can explicitly post Windows’ application quit message, WM_QUIT using the function PostQuitMessage. This function gets the exit code as its parameter. After sending the message, your message dispatching loop should wait for the WM_QUIT message and then break.

A note about Qt

To a Qt programmer, all this WinMain stuff sounds unnecessary. Almost all Qt examples just use a main function and everything still works as expected (so you don’t have a console window open all the time). I was curious on how they achieve that and did a little digging. It turns out that if you specify to build your application as a graphical application and you don’t create a WinMain, you will get a linker error unless you link a special library to your program. This suggests that this special library contains the WinMain function which calls the user main function. If that is the case, then the Qt guys rely on undefined behavior, since even forward-declaring main is prohibited, let alone call it from the outside. One might correct me if I missed something here.

This entry was posted in Uncategorized. Bookmark the permalink.

12 Responses to Some main facts

  1. aedf says:

    I am sure this paragraph has touched all the internet viewers, its really really nice post on
    building up new website.

  2. That is really attention-grabbing, You are
    an overly professional blogger. I have joined
    your rss feed and look forward to in search of more of your excellent post.
    Also, I have shared your site in my social networks

  3. gucci 2011 春夏 バッグ 中古品店、アンティーク
    ショップが見つかりますおよびファッション店をすべて症候性道路。パターン財布のになってしまうイベントを保つことを選んだ心配。べきであるないされる、違いディメンションで.今まで ! グッチ バッグ ショルダー

  4. impetigo says:

    Before applying this topical treatment for Impetigo, one should clean the affected
    area first with warm water and antibacterial soap. With the use of soap and water or antiseptic,
    these breaks in the skin are kept clean. The preparation would
    highly depend on the state of your condition.

  5. This iis really interesting, You are a very skilled blogger.
    I’ve joined your rss feed and look forward to seeking more of your excellent post.
    Also, I’ve shared your site in my social networks!

  6. Its not my first timе to pay a viаit this site, i am visitig this web site
    dailly and take gooԁ faqctѕ ffrom hеre everyday.

  7. With havin so much content do you ever ruun into any issues of plagorism or copyright infringement?

    My blog has a lot of uunique content I’ve either created
    myself or outsourced but it appears a lot of it is popping it up all
    over the web without my agreement. Do you know any techniques to help stop content from being
    stolen? I’d genuinely appreciate it.

  8. This paragraph will assist the internet people for setting up new website or even a blog from
    start to end.

  9. I’m truly enjoying the design and layout of your blog.
    It’s a very easy on tthe eyes whic makes it much more enjoyable
    ffor me too coe here and visit more often. Did you hire out a designer to create your
    theme? Excellent work!

  10. The other day, while I was at work, myy sister stole
    my apple ipad and tested to seee if it can survive a 25 foot drop,
    just soo she can be a youtube sensation. My apple ipad iss now destroyed and she has 83 views.
    I know this is entirelyy off topic but I had to share it with someone!

  11. I drop a lave a response each time I especially enjoy a post on a blog
    or iff I have something to add to the conversation.

    It is triggered by the sincerness communicated iin the article
    I read.And on this article Some main factss | pimiddy.
    I was aactually moved enough to post a thought 😉 I actually do have a couple of questions for you if
    you don’t mind. Could it be only me or do some of these comments come across like they are coming from brain ddead
    people? 😛 And, iff you are posting on other sites, I’d like to follow you.

    Could you make a list every oone of all your community sites like your
    Facebook page, twitter feed, orr linkedin profile?

  12. Pretty section of content. I just stumbled upon your site
    and in accession capital to assert thatt I acquire actually enjoyed account your blog posts.
    Any way I will be subscribing to your augment and even I achievement you access consistently quickly.

Leave a comment