I know that in computers, value returned by the main()
function is received by the operating system.
But, what happens in the main()
function of a microcontroller?
-
9I always use void main() when I am using C for PIC microcontrollers. When using C compilers for microcontrollers, it really does not matter at all. Because there is no operating system that runs the (say) "main.c". If there is something like RTOS running in that microcontroller, then the operating system is the "main.c". – abdullah kahraman Jan 22 '13 at 16:44
-
7Not realy a duplicate, but at least related: http://electronics.stackexchange.com/q/30830/4950 – PetPaulsen Jan 22 '13 at 17:15
-
2How the startup function is defined is usually not up to you to decide. The environment you're using will document the supported startup function forms. Hosted C implementations are required to support two forms of `main` with two different signatures, both of which return `int`. If you're using a freestanding C implementation, that implementation dictates how you should write the startup function. You can't write a `void` returning function just because it doesn't return. The *behavior* of not returning is different from the function *type* which influences the overall calling conventions. – Kaz Jan 23 '13 at 00:43
5 Answers
On a microcontroller, main()
is not really expected to ever exit, and the behavior if it does is not defined — so it's up to whoever wrote the C runtime for the microcontroller. I've seen systems that:
- Have an implicit loop around
main()
, so that if it exits, it simply gets called again. - Have a simple "jump-to-self" loop (or a HALT instruction) that gets executed if
main()
ever exits. - Simply execute the rest of code memory that follows the call to
main()
. This is called "running off into the weeds".
I've never seen one that actually does anything at all with the value returned by main()
. If this is something you actually care about, then you should take a look at — and possibly modify — the source code for your system's C runtime library.

- 168,369
- 17
- 228
- 393
-
10The C standard defining `main()` to have an `int` return value obviously was not designed with an OS-less microcontroller in mind. So this is unspecified behaviour and anything might happen depending on your C runtime, as Dave listed. – ndim Jan 22 '13 at 17:32
-
4C running on an OS-less microcontroller could be considered a freestanding implementation, and the C standard doesn't even require a freestanding environent to *have* a `main()`, much less define its return value. That's up to the implementor. – KutuluMike Jan 22 '13 at 21:05
-
2@ndim - to split hairs, `void main( void )` is *implementation defined behaviour*, not *unspecified behaviour*. – Andrew Jan 22 '13 at 21:42
-
4@MichaelEdenfield: Indeed. However, *all* code in C is defined in terms of functions, so it is never possible to have a fresstanding system *completely* written in C; there needs to be at least a tiny bit of assembly language (or whatever) that sets up a minimal environment so that a C function can be called. The most obvious name for that function is `main()`. – Dave Tweed Jan 22 '13 at 22:08
-
@ndim The C standard says that any form of main is fine. Have you read the standard? More importantly, have you checked which sub chapter the section specifying `int` format of `main` is located in? 5.1.2.2 Hosted environment. – Lundin Jun 20 '16 at 06:51
-
At least *my* startup code will proceed to call the global destructors so anything written to stdout still gets flushed. – Simon Richter Jun 20 '16 at 07:54
-
To explain @Lundin's comment above: The C standard knows a "hosted environment" (i.e. the program running properly on a proper OS) where `main(...)` *shall* return an `int`. And then there is the "freestanding environment" (i.e. program basically running on bare metal) where "name and type of the function called at startup are implementation-defined". So anything goes in your microcontroller environment, and you'll best look at what the startup code included in the binary generated from your source code actually does with a disassembler. – ndim Jun 30 '16 at 01:40
-
@ndim Even in a hosted implementation (which is probably irrelevant for most EE questions in the first place) main is allowed to be declared in implementation-defined forms. The standard is not really clear. [Details here](http://stackoverflow.com/a/31263079/584518) – Lundin Jun 30 '16 at 06:35
-
I have also encountered a HALT instruction after the jump to main, if you return the thing freezes. – istepaniuk Apr 04 '20 at 15:57
-
1@istepaniuk: That's equivalent to my second bullet. Not all microcontrollers have HALT instructions. – Dave Tweed Apr 04 '20 at 16:07
A common misunderstanding/myth is that int main
is the only valid form specified by the standard. That is not true.
The C standard speaks of two implementations: hosted and freestanding. "Implementation" in this case means compiler. Hosted compilers compile for a specific OS and freestanding compilers compile for a specific bare metal application. Embedded systems are almost always freestanding systems - even in the case of RTOS.
Freestanding implementations may use any form for main()
, they don't even need to have a function called main. Most often, they use the form void main (void)
, since it doesn't make any sense to return anything.
What's important to realize here is that it is always the compiler that decides the form of main()
and never the programmer.
Freestanding implementations that do return something from main()
are very questionable. Makes you wonder if the people who made the compiler actually read the standard...
For those using gcc, make sure to compile as -ffreestanding
or otherwise the compiler defaults to "PC mode" (hosted). This will also impact whether the compiler inlines standard library functions or not, which you sometimes wish to avoid in high integrity embedded systems.

- 17,577
- 1
- 24
- 67
The C language standard allows for the implementation defined variation void main( void )
and this is the usual form in embedded systems - simply because they are not expected to return.
If you look at the compiler setup, there is usually a bootstrap snippet of code, called from the reset vector, which performs some basic initialisation (including eg coping of initialisation values into variables) before calling main().
This will also (usually) be within an infinite loop, or perhaps perform a reset, if main()
returns

- 259
- 6
- 20
You cant get to C without a bootstrap the bootstrap code, ideally written in asm (if in C you have a chicken and egg problem), prepares the environment to run C sets up a stack, preps .data, preps .bss, then calls main. Some folks think main never returns but the beauty of baremetal is that is all up to you. You can certainly return from main in an mcu, say for example when something fatal happens, or if your design is 100% event (interrupt) driven, setup all the peripherals and interrupts then return and have your bootstrap spin in an infinite loop which is prettier than a while(1) continue; in the main() code.
A lot of folks use canned libraries and environments to make baremetal not feel like baremetal and while are responsible for what is going on underneath dont look to see nor control it. Again part of the beauty of baremetal.
What happens when main returns depends on the system be it an operating system or baremetal on an mcu. A wise design would allow for main to return in the bootstrap and then depending on that design choose what to do, at a minimum go into an infinite loop.
main() is just another function name to the compiler for the most part, some toolchains will add extra stuff when it sees that function so you should use that function with care in baremetal to avoid excess baggage in such a resource restrained environment. Not unwise to use a C entry point other than the name main() to avoid that problem, but naturally that usually involves taking control over the whole thing rather than just adding a few lines to someone elses sandbox (writing your own bootstrap and possibly linker script and as a result possibly the libraries (not a bad idea) as well).
Once you take over you can certainly have the return value from main mean something, again in the case of an interrupt driven design main() could simply start things in motion and then return to an infinite or wait for interrupt loop in the bootstrap, but you could choose to have the return value cause action to be taken if not zero then clock gate or put all peripherals in reset for a low power failed startup.
Short answer: The return value goes into the ether as generally you land in an infinite loop either directly or after the bootstrap has assumed something bad happened and shut down or un-done whatever you configured in your portion of the application. Any sandbox that doesnt allow for main() to return is a sandbox to avoid using, it should at least have minimal support (an infinite loop).
Shorter answer: Who receives the return value? You do as you are responsible for this application and as a result the bootstrap.

- 8,203
- 24
- 33
It (as other answers mentioned) depends on your toolchain, but for example in GCC main
is compiled as other functions, so its return value will be stored accordingly to calling conventions (on ARM I am using right not with GCC it will be put to R0 just before return).
I guess that is is simillar on AVR-GCC, so custom script can use this value after main returns.

- 241
- 1
- 7
-
-
It emphasizes that the one who calls `main` can get its return value. Of course it is ignored in 99.9% situations, but answer provides information who can receive this return value. – kwesolowski Aug 08 '14 at 22:08