0

I know that when I write a C program on UNIX o/s, the compiler will automatically add variables argc and argv to the program if I do not declare them. But if I write a C embedded program — for example, an embedded program for ARM microcontroller, or a C program in RTOS or a C program in Window OS — does the compiler automatically add argc and argv in my program?

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • `argc` and `argv` are arguments for an alternate main method signature. It's hard to see how those get added "automatically;" you add them to your main method (in your source code) if you need them. – Robert Harvey Mar 03 '20 at 15:42
  • In any case, this is easy enough to test. Whatever behavior you think you got in your "standard" compiler, repeat the process with you embedded compiler and see if you get the same behavior. – Robert Harvey Mar 03 '20 at 15:45
  • See [What should `main()` return in C and C++?](https://stackoverflow.com/a/18721336/15168) for the standard definitions of 'hosted' and 'freestanding' environments. Often, an embedded system constitutes a 'freestanding environment' where the standard does not legislate much about what's required. What's allowed and provided depends on the o/s — look at its documentation. I'm not aware of a system that formally adds `argc` and `argv` to `main()` if you write `int main(void)` — I think you have a misunderstanding, maybe because the startup code probably emulates `exit(main(argc, argv, envp));`. – Jonathan Leffler Mar 03 '20 at 17:46

2 Answers2

4

Your initial assertion is incorrect. There is no automatic addition of variables. The operating system will provide an argument vector and count which will be conveyed to your code via the arguments to main() if you declare them. Those arguments need not be called argc / argv - that is merely a convention.

In a "stand-alone" (i.e. not hosted by an OS) program - which is most, but not all embedded systems - the C runtime start-up may pass arguments to main(), but in most cases will not do so. Whether it does or not, declaring main() with arguments remains valid and must compile, but the content may be undefined.

There may be cases where you might pass arguments to main() from the runtime start-up, in order for example to convey information from a bootloader perhaps. However such behavior would normally be for you to write and implement by customizing the start-up code unless perhaps you were using some third-party embedded framework that behaved in such a way already.

That is to say you can use arguments to main() in your stand-alone embedded application if it suits you, but you are likely to have to implement that behaviour yourself.

Typically in a bare-metal system, the developer has access to the source code for start-up, and can customise it, or the runtime provides stubs/weak-links or similar for customisation. The runtime generally requires a stack to be provided, statics to be initialised and little else. The standard library init will normally be called to establish such things as a heap and standard I/O streams for example. Parameters for main() can also be established here if necessary.

See for example the start-up for the armcc compiler described here. In section 4.4 __rt_lib_init which in turn calls __ARM_get_argv() (§5.15) which processes _sys_command_string - if you provide _sys_command_string to the linker, main() will receive standard arguments.

Clifford
  • 76,825
  • 12
  • 79
  • 145
  • As far as the C standard is concerned, the compiler is responsible for stating the valid form(s) on main(), never the programmer. Though of course most embedded compilers don't treat main() as a special snowflake, but just like any other function. – Lundin Mar 03 '20 at 16:02
  • @Lundin Indeed. I should perhaps insert an "in practice" in there somewhere. – Clifford Mar 03 '20 at 16:14
2

On so-called "bare metal" (no OS) or RTOS systems, it doesn't make sense for main() to have any parameters, nor does it make sense to return anything.

The C standard calls these "freestanding systems" and most commonly they use an implementation-defined form of main, in the form of void main (void). (With the gcc compiler you need to compile as -ffreestanding for embedded systems).

Typically it goes like this:

  • Power-on reset, calls ->
  • Reset vector, calls ->
  • "C runtime" (CRT) start-up code, calls ->
  • main()

However, many CRT are written by amateurs so you might have to roll out your own one, or else you might get stuck with some braindead CRT running the whole .bss/.data initialization with some slow on-chip RC oscillator. A list of things to consider when writing a useful CRT here: https://stackoverflow.com/a/47940277/584518

Lundin
  • 155,020
  • 33
  • 213
  • 341