void main(void)–the argument continues…

For, what must be, years now the perpetual argument among programmers in various forums  resurfaces about the legality, or not, of the use of void as the return type for the main function.

I generally try and ignore these arguments as it seems such a trivial point, but maybe it’s because yet another birthday has just passed it’s time to put my two-penneth in.

Before we start, hopefully we all agree that the following code is an abomination:

main() { }

You would probably be more shocked if I told you that this comes from a fairly recent book (2008) Programming 32-bit Microcontrollers in C. Shame on you all.

The argument against the use of void has weighty backing, notability the creator of C++, no other than Bjarne Stroustrup himself. The following is taken from his very useful C++ Style and Technique FAQ:

The definition

void main() { /* ... */ }

is not and never has been C++, nor has it even been C. See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1. A conforming implementation accepts

int main() { /* ... */ }

and

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

He then goes on to state;

A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers

Ouch; ignorant, moi. I take that as a personal insult Winking smile

Unfortunately, what Prof. Strousturp is failing to recognise (or possibly acknowledge) is the hundreds, if not thousands, of embedded programmers out there. For example, in the text above the key line is:

The int returned by main() is a way for a program to return a value to “the system” that invokes it.

Deeply embedded systems don’t have a “system” that invokes it, so there is nowhere for it return to and thus having an int return type is actually misleading.

However, I hear you shout, the standard says it must be; not so.

Both the C and C++ standards define two types of execution environments

  • Freestanding
  • Hosted

To quote the C standard (5.1.2.1):

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation defined.

and the C++ standard (3.6.1)

It is implementation-defined whether a program in a freestanding environment is required to define a main function

So there you have it your honour, embedded systems are freestanding, and as so I can call the startup anything, therefore void main(void) is perfectly legal.

So I would argue, not understanding that void main(void) is perfectly acceptable in for freestanding environment is in risk of being considered ignorant of the finer details of embedded programming.

I know this won’t be the end of it but next time someone starts down this line, just ask them to explain the difference between a freestanding and hosted environment (makes a nice interview question as well!).

Niall Cooling

Director at Feabhas Limited
Co-Founder and Director of Feabhas since 1995.
Niall has been designing and programming embedded systems for over 30 years. He has worked in different sectors, including aerospace, telecomms, government and banking.
His current interest lie in IoT Security and Agile for Embedded Systems.
Dislike (0)

About Niall Cooling

Co-Founder and Director of Feabhas since 1995. Niall has been designing and programming embedded systems for over 30 years. He has worked in different sectors, including aerospace, telecomms, government and banking. His current interest lie in IoT Security and Agile for Embedded Systems.
This entry was posted in C/C++ Programming and tagged . Bookmark the permalink.

8 Responses to void main(void)–the argument continues…

  1. Georg Nikodym says:

    I have no quibble with your reading of the standards. However, embedded systems are not necessarily freestanding. Often they are built on top of embedded operating systems such as Linux, QNX, VxWorks, etc. And the applications running in these environments are far from standalone. They are hosted.

    Really depends on whether you are writing software for a microwave oven or a Linksys router.

    Like (1)
    Dislike (0)
  2. Hi Georg,
    Of course, I would say the majority of embedded systems software developed is probably targeting an operating system of some kind. However, there is a distinct difference between those that use a "process" model and invoke programs into execution (e.g. Linux) than those that generally don't use that model (e.g. Nucleus). It come down to whether having an int return is useful or meaningful. In some cases it is, then use the int, in many cases it's not, so don't.
    Thanks for the feedback,
    Niall.

    Like (0)
    Dislike (0)
  3. Brian Hooper says:

    Great article Niall. I shall squirrel away those C/C++ standards refs 🙂

    You are of course spot-on by saying that a return value is meaningless if the main is going to be top of the call tree. The only counter argument I can think of is that you are building a barrier against re-use. Should you ever want to deploy the same main in a different system where it will be called, you face a bit of rework.

    Like (0)
    Dislike (0)
  4. craniumonempty says:

    "The int returned by main() is a way for a program to return a value to “the system” that invokes it."

    This says it right there. If a system invokes the program, then it should be "int main()". If you are on a freestanding environment, it's defined by implementation, so it's true you can call it "void main()" but that's not truly the same thing, is it? You don't even have to call the function "main" in a freestanding environment. So in a way, you are both right, aren't you?

    Like (0)
    Dislike (0)
  5. Dan says:

    Two points.

    1) As Brian Hooper says, code that is tailored to a specific environment is inherently non-portable. If I build code & do certain things because it's a freestanding environment, then that code might not work on a PC, in workstation environment, etc.

    2) I've many times written code for freestanding systems where I not only used the return from main (to pass back information - sometimes an exception - to the runtime / startup code, presumably for some type of controlled shutdown/restart), but I've also passed information into "main" (or whatever you call it, it's freestanding, after all)... perhaps info like warm/cold boot, etc... sure this could be done w/ globals, but to me it seems ugly & it also complicates testing outside of the freestanding environment.

    To each his own. Niall, you seem to be a little annoyed by the topic (based on the language in your post), but it's important for people to understand the ramifications of deviating from compliant/conformant code (I don't recall the distinction right now). I have no doubt *at all* that you understand these concerns, but some of your readers may not.

    I think a good, objective, technical discussion is always a good thing - even when the topic isn't super challenging & insightful.

    Thanks for your blog posts, they're always a pleasure to read.

    Like (0)
    Dislike (0)
  6. Norman says:

    To me there is no difference between what you call a Hosted vs. Freestanding environment because even Freestanding environments are Hosted by some sort of
    c-startup code.
    Every tool provider does this in their own particular way... but there always is some initial environment.
    Therefore all environments can be considered Hosted... Even a microwave!

    Norman

    Like (0)
    Dislike (0)
  7. maarten says:

    I agree: if you don't need more then why should you declare more?

    However, there are ssome considerations:
    In this case the program result value will probably be random (perhaps depending on the used compiler or compile options). Using murphy's law this WILL cause a problem when the program result value can be used.

    Also, i don't know whether using void main(void) has any advantage: a good optimizing compiler should remove the superfluous code used for assigning a return value when the code is compiled for a freestanding environment, so why take the risk?

    So, as a rule I would say: always declare return type int for main UNLESS you know what you're doing.

    Like (0)
    Dislike (0)
  8. Hi Norman,
    The terms Hosted and Freestanding are from the standard. I try to look at it as if "can more than one C program be executing at the same time?". In the smaller, deeply embedded systems, the answer is typically "no". The waters get muddy when we get into embedded Linux etc. In those environments (hosted) the use of int makes perfect sense. Outside of that it normally wouldn't, however there have been some good points about both the return of int and even the use of argc.
    What I hope we can draw from this is that both void main and int main both make sense in the context of embedded programming - use what is appropriate for your system. As Dan said "to each his own".
    Niall.

    Like (0)
    Dislike (0)

Leave a Reply