Nor did BASIC.
I think the better question is, why does C (and C derived languages) require a main function?
Nor did BASIC.
I think the better question is, why does C (and C derived languages) require a main function?
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.
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.
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.
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.
> ... 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.
The following is a completely valid .f90 file that will compile and run:
program p
do i=1,10
print *, "Hello World ", i
end do
end programI compiled the above program with gfortran, and then objdump -d on it, and found this line:
0000000000001283 <main>:
Under which the cli flags are setup, and then the program is called with: callq 1199 <MAIN__>
Which is where you'll find the "true" main function.To my mind, the question is "does the programmer have to write a main function", not "does the toolchain create a main function somewhere".
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.
How would you place Bash scripts then? Or a JITed language like Julia?
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.
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.
So... why does it?
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?
I used it to inject my own code in foreign executables.
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:
MS-DOS has "autoexec".
Unixes run /sbin/init.
U-Boot has the bootcmd variable.
All of these are like a main startup function.
Specifying a startup function when saving an image is usually optional in Lisp.
Also the capability to save an image is optional: Lisp systems without saving images: ECL, mocl, ABCL, ...