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() { }
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.
I am sure this paragraph has touched all the internet viewers, its really really nice post on
building up new website.
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
gucci 2011 春夏 バッグ 中古品店、アンティーク
ショップが見つかりますおよびファッション店をすべて症候性道路。パターン財布のになってしまうイベントを保つことを選んだ心配。べきであるないされる、違いディメンションで.今まで ! グッチ バッグ ショルダー
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.
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!
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.
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.
This paragraph will assist the internet people for setting up new website or even a blog from
start to end.
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!
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!
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?
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.