> Many languages start running your program by calling a function of yours that must have a specific name. In C (and many C derived languages), this is just called main(); ... Python famously doesn't require any such function...
Nor did BASIC.
I think the better question is, why does C (and C derived languages) require a main function?
Yeah, this was a funny thing for me to read because my first language was BASIC. When I encountered C, it was the surprising and unique one because it required all code to be inside a function body.
I suppose one of the advantages is that it makes separate compilation easier. Take several .c files, compile each one separately into a .o file, and what happens when you try to link them together? Where should you start executing?
Defining a function with a well-known name is a simple, easy solution to that.
Python doesn't need to solve this problem because you give it a file to start interpreting, and that file is the entry point. You either pass the file name as an argument or you use #! and that determines it.
Maybe it goes back to assembly language. If you look at a block of assembly or machine language code, it's not immediately obvious where the entry point is. You could guess that it's at address 0, but I don't think that's the case for all machines.
I think more important than having a main() function is to clearly delineate the entry point. And in Python, the entry point is the first line of the program.
Now I've noticed, at least within the Arduino environment, that it uses setup() and loop() as entry points, but code is executed before these functions are called, if for instance a global variable definition invokes a class constructor. I don't know if that's how "regular" C works, but it creates ambiguity as to what runs first.
There are other languages where it seems hard to guess where the actual entry point is located unless you know where to look.
You don't get quite this level of ambiguity with standard C, since it does not provide objects (in the object-oriented sense) with constructors, in contrast to C++ that is used in the Arduino environment (in C++, initialization order of global objects is indeed unspecified, inviting subtle bugs to occur).
However, many common C compilers provide methods to declare functions to run at initialization time, e.g. gcc's `constructor` attribute, opening this rabbit hole also for C programmers.
Regular C will start someplace in the runtime library, which will do things like global initialization. Once that's complete the library will call main().
The OS determines the exact place in the code where execution starts. In DOS I think it was address 0x100. A more modern OS probably uses an executable file format that allows you to specify the starting address.
The actual entrypoint to your program is defined in the elf header of whatever binary you execute (doesnt matter if it is C or not). Usually this entrypoint points to a function in libc, which after some setup calls the user's main function.
> ... why does C (and C derived languages) require a main function?
It doesn't actually. You just need an entrypoint defined for the loader. In practice most people don't want to go around libc, or fiddle with internals. This is much less true in embedded development, where main functions are sometimes missing.
Why doesn't C have global code? Probably because it would require abstracting further away from the generated assembly. If there is code that is not inside a function, how can it be called? Remember that the entry point to all programs is a function.
Disclaimer: I know very little about python internals.
There is a "true" main function for the interpreter itself. From the perspective of the operating system and CPU the python code isn't executing, the interpreter is.
But conceptually, the python code probably doesn't have main function. Python code is so far abstracted from how the CPU works that it likely doesn't need one.
Just seems kinda odd to me to distinguish Fortran and Python here, based on how many levels of abstraction there are between the user code and the main function.
How would you place Bash scripts then? Or a JITed language like Julia?
I only mention python because people were asking about it. There are really two unrelated questions here:
1) How does a computer start executing your code?
2) What kind of abstractions does a programming language provide?
Having anything other than a function as a top level most likely implies an abstraction. The second question is pretty much irrelevant when you are talking about interpreted languages. These aren't programs at all from the perspective of the CPU.
JIT is a different story, and one that I know very little about on a technical level. What happens when pypy generates some native code? Does it generate a function and call it? Does it have some other convention? I have no idea.
Python doesn't require you to have anything around a series of statements. Whereas Fortran requires at least one top-level program block. That program is a top-level function, which directly corresponds to MAIN__ in compiled code.
I ran the test. The assembly for that program creates a "main" function, which contains fortran init code, and a MAIN function that contains the user code.
IIRC it is the C runtime that calls main(). In C all executable code must necessarily be inside functions, so inevitably there had to be one 'special' function that was called first.
> I think the better question is, why does C (and C derived languages) require a main function?
That's not the true question. The true question would be, "why does C (and C derived languages) require that all executable statements be within a function?" Requiring a main function is just a natural consequence of that.
Because it's simpler. C has no concept of nested functions. If it instead allowed users to write the statements of 'main' at the top level, it would complicate the rules for scoping. For example, can an inner function access the outer function's variables? Or jump to its labels? Simpler just to say: all statements live in a function; functions don't nest; and execution starts at main.
At a guess, it makes the language simpler. The main function is just that -- a function. It uses tools the language already has to deal with arguments and exit codes.
Arguably it also keeps things like libraries simpler. For example, what would it mean for me to have top-level expressions in a dynamic shared library? Does it execute as soon as I dlopen() it? Does it create an implicit "init" function that I need to call?
Parts of the C runtime library need initialization and this initialization is done before main is called. Interpreters do this before you have a chance to run any user supplied code.
C needs a main function because all matters of initialization and module dependencies are left to the programmer.
C has no concept of a module A depending on a module B.
If C had top-level executable statements, there would be no defined order to them. Proof: C++ has a way of executing top-level code via file-scope definitions of class objects that have constructors and destructors. The order of these is not defined by the language; it depends on how the object files are linked and such.
Niklaus Wirth's Modula-2 language has syntax for this. Every module has an anonymous block of code that is executed at program initialization time. Modules declare dependencies on each other, which cannot be cyclic. If A uses B, then B's initialization code runs before that of A.
There is a single root module on which nothing else depends, and that module's initialization code runs last. In that initialization code, you can do the application startup; it serves as main.
I think some other languages in this broad family are that way also, like Ada.
Embedded systems written in free-standing dialects of C still choose to start with a function, though not necessarily main. For instance, Linux starts with a function called start_linux. That's not the first code in vmlinux that executes; there is assembly code above that, typically in a module called entry.S which will branch to start_linux.
This is very easy for embedded developers to deal with. There is no special run-time support for special global initialization blocks: it's just a function call named by a symbol that you can use as a branch target in the assembly code.
Even in Lisps, though there is no concept of a main function in dialects like Common Lisp, you have to specify a startup function when you save an executable image. This is because an image is not simply a collection of top-level forms that are to be evaluated, unlike a Lisp source file. It is not obvious what has to be executed when the image is restarted. Without the ability to specify a startup function, a possible choice might be this: that when the image is re-started, the save-image function (whatever it is called in the implementation) will appear to return a second time, perhaps with a value indicating "I'm now returning again due to the image being re-animated".
A compiled C program, at a very basic level, is analogous to a Lisp image.
A Lisp system loads some .fasl files, and then saves an image, specifying a startup function.
Similarly, in a C toolchain, the linker loads .o files and saves an image, recording in that image an entry point where to begin executing.
As a rule of thumb, if it's image-based, then it probably needs a startup function, unless there is an equivalent language feature for handling startup.
That applies to the booting of operating system images also. Consider:
Nor did BASIC.
I think the better question is, why does C (and C derived languages) require a main function?