1

In a course I am taking on embedded systems, I program LPC1768s which are based on ARM using C.

The main functions are always of int return type. However, there are no return statements in the end like a return 0.

I believe this may have something to do with the infinite while loops that these programs use because the controller is never really able to reach the return statement. However, I thought that as this is C, there might be errors, though it turned out that I was wrong.

Just for reference, here is the main function of a program trying to use UART

int main (void) 
{
    SystemInit();

    GPIO_PinDirection(LED1,OUTPUT);        /* Configure the pins as Output to blink the Leds*/
    GPIO_PinDirection(LED2,OUTPUT);

    TIMER_Init(0,100000);                  /* Configure timer0 to generate 100ms(100000us) delay*/
    TIMER_Init(1,500000);                  /* Configure timer1 to generate 500ms(500000us) delay*/

    TIMER_AttachInterrupt(0,myTimerIsr_0);  /* myTimerIsr_0 will be called by TIMER0_IRQn */
    TIMER_AttachInterrupt(1,myTimerIsr_1);  /* myTimerIsr_1 will be called by TIMER1_IRQn */

    TIMER_Start(0);                         /* Start the Timers */
    TIMER_Start(1);

    while(1)
    {
        //do nothing
    }
}
Paddy
  • 225
  • 1
  • 8
  • 1
    This isn't really a question, but I think I know what you're trying to figure out, and I think this will help: [What happens when an embedded program finishes?](https://electronics.stackexchange.com/questions/30830/what-happens-when-an-embedded-program-finishes) – Tri May 26 '19 at 01:54
  • 2
    There's no particular need for a return statement from main() in embedded code. In general, you do NOT want to return from main in a stand-alone environment because what happens afterwards isn't defined. (It usually returns to the start-up code, which was written in assembly in a file called crt0.asm [or similar.] And that code usually just sits in a tight loop doing nothing if main() returns.) If there is no return with a value, some compilers return whatever is in the associated register (unpredictable) or zero, perhaps. – jonk May 26 '19 at 01:55
  • Got it. So the question is, why use 'int main()'? It would probably make more sense to use just 'void main()' here – Paddy May 26 '19 at 01:59
  • 1
    the C standard says that main returns int. This is required. Google C99 or a newer version of the ANSI C standard. – user57037 May 26 '19 at 02:23
  • 1
    Another relevant question: [Who receives the value returned by main()?](https://electronics.stackexchange.com/q/55767/11683) – Dave Tweed May 26 '19 at 18:28
  • when you write a return statement after the while loop you will get a compiler warning that the return statement is inaccessible obviously due to infinite while loop. That's why no return statement. – Vaibhav May 27 '19 at 09:32
  • @Vaibhav Though omitting the return statement on an old C90 compiler would mean that this code invokes undefined behavior. So it better and more portable to use a form of main() that doesn't have a return type, such as `void main (void)` supported by most compilers. – Lundin May 27 '19 at 09:40
  • see when it comes to embedded c there is lot of things going in controller before execution of the main() function. Thus main function should have return type for going back to those setup or address in case of execution ended. – Vaibhav May 27 '19 at 09:57
  • 1
    @Vaibhav No, returning to the CRT doesn't make any sense in a freestanding system. At best there's an eternal loop there. But you might as well get run-away code executing random crap in flash. In my experience, the latter is the most common. – Lundin May 27 '19 at 10:46

3 Answers3

4

The C standard allows two forms of environments: freestanding and hosted.

  • Freestanding systems refers to systems without a file managing OS, such as microcontroller applications, RTOS applications, applications that are the OS.
  • Hosted systems refer to programs running on top of an OS: Linux, Windows etc.

Hosted systems, and only hosted systems, have a special requirement that they must support the standard form int main (void). So when we have the flood of PC programmers entering the branch of embedded systems programming, they bring religious dogmas about main with them, because they only know the hosted system requirements.

But since almost all embedded systems are freestanding systems, we can completely ignore hosted systems here on the E.E site. This is what the standard says:

ISO 9899:2018 5.1.2.1 Freestanding environment

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.

Implementation-defined means that the compiler decides which forms that are allowed. The most common form is void main (void). Some systems don't even have main, others may name it differently. What's important to realize is that the compiler makes this decision, never the programmer.

The problem with using int main (void), if supported by the compiler, is that it almost certainly generates calling convention overhead. The reset vector and CRT will call main and push data onto the stack, that just sits there as dead space. Various dirty hacks (like manually resetting SP) can dodge this waste, but generally int main (void) is bad form in freestanding systems, since it is wasteful and senseless. What are you going to return to?

If there is a return 0 or not in the end doesn't matter. In standard C, int main(void) is a special function and if missing a return statement, it is equivalent to writing return 0. Only the int main (void) function has this special rule, not ordinary functions.

So what you should do is to check which forms of main() your compiler allows, then pick one without a return statement. For example, gcc supports the preferred form void main (void) if you compile with gcc -ffreestanding.


For more information:

I wrote a detailed answer on Stack Overflow, addressing both freestanding and hosted systems, in all versions of the C and C++ standards (at the point when it was written): What should main() return in C and C++?

Lundin
  • 17,577
  • 1
  • 24
  • 67
2

It really doesn't matter in practice what main() says it will return in this instance. Unless you enable strict C in your compiler flags, the compiler probably won't care what return type you gave it (so long as it is a supported format in said compiler).

Why does main() historically return int? Because the return value is supposed to tell the caller if the program completed successfully.

In this case, it will never return and also should never return, since there is almost certainly nothing set up to handle the main() function returning. Thus the return value is completely irrelevant. If you want it to return void, then do that. It won't make any real difference. Your compiler might generate slightly different assembly code, which may even be slightly less/more efficient than the code it would generate if you used int.

A void return type is supposed to define a function that 'returns normally' but doesn't provide a result. In this case main() does not return normally, it in fact never returns. C11 does actually provide a standard for defining a function that doesn't return (_Noreturn). I wouldn't want to bet on your compiler actually supporting it though. If it did, then the most correct deceleration would be: _Noreturn void main(void).

C also specifically declares that execution reaching the end of main() without encountering a 'return' statement will return a value of 0. So it's not a syntax error to have no return statement in your main() function.

I don't think this is super relevant to the OPs question. But as pointed out in the comments, it's not strictly true that the definition doesn't matter. It wont change the logic of your program, but it certainly could have a tiny impact on how efficient the generated code is. If you are worried about this then you need to find out what your specific compiler generates for the various definitions of main() that it supports.

hekete
  • 1,376
  • 6
  • 12
  • " the compiler probably won't care what return type you gave it" This is incorrect. The _programmer_ can never chose the format of main(). The compiler supports a number of forms, including the standard `int main (void)` and then likely some implementation-defined forms. – Lundin May 27 '19 at 08:14
  • "Declaring void main() isn't actually any more correct than int main()" Sure it is, if the compiler supports that form and you don't want the calling overhead code generated. – Lundin May 27 '19 at 08:15
  • @Lundin anything that doesn't follow the from 'int main(void or int, char*)' is implementation dependent. We are talking about a compiler for embedded systems, which means it is probably designed to generate code assuming main doesn't return regardless of what return type you give it. Now if you were really worried about what code was generated, then you are either going to know what your compiler does with different definitions or you will write it in assembly. In practical terms, it doesn't matter what you defined as main, so long as it compiles. – hekete May 27 '19 at 09:07
  • "which means it is probably designed to generate code assuming main doesn't return regardless of what return type you give it". I have never seen a compiler do this, you have to check the calling CRT as well as main(), different systems with different calling conventions. Almost all systems generate overhead code if you use `int main (void)`. – Lundin May 27 '19 at 09:22
  • I have seen embedded system compilers generate correct efficient code for 'int main(void)', while producing less efficient variations for different forms. Probably because the code base came from somewhere else and they developers didn't bother to optimize the other variants they assumed no one would use. But there are so many compilers, plenty probably generate overhead. I don't think the OP really cared if their compiler used a JMP or a CALL to get into main and if it reserved a tiny bit of extra stack space or not... – hekete May 27 '19 at 09:40
  • In practice if the CRT use `asm JMP;` or such, it may or may not require stacking. A C compiler may still stack CCR and similar no matter. But most CRT nowadays seem to use a C call `main();` which will generate `CALL`. – Lundin May 27 '19 at 09:42
  • "C does not provide a standard definition for a function that never returns (afaik?)" Standard C does btw support `_Noreturn`, allowing us to write `_Noreturn void main (void)`, but 8 years later C11 is still "new" and I'm not sure how many embedded compilers that support such forms. – Lundin May 27 '19 at 09:45
  • I wasn't aware that _noreturn was part of the standard? I know it's supported by GCC, but I wouldn't have expected your average embedded system compiler to have it. Regardless, I don't think any of this is relevant to the OPs post. But I will update some of my answer to be clearer... – hekete May 27 '19 at 09:51
-1

Suppose you were to put

return 0;

after the

while (1) {}

How would you expect it to change the behavior of your program?

The Photon
  • 126,425
  • 3
  • 159
  • 304
  • 2
    It wouldn't make any difference. That's because the return statement would never be encountered. So that begs the question, why explicitly say that the main function will return an integer while it could simply use void as the return type – Paddy May 26 '19 at 02:00
  • 1
    One compiler I used would throw up a warning if you put `return 0` at the end of your code if it was clearly unreachable. – Warren Hill May 27 '19 at 10:26