C Part I · Module 1 · Understanding C and How Programs Work
What Happens When a C Program Runs
Module 1 has followed a source file all the way to an executable. This chapter follows it one step further, into the gap Chapter 1.6 opened at the end: an executable is a file sitting on a disk, and a running program is not a file at all. Closing that gap settles three things earlier chapters deliberately left open — where the library code that never made it into your executable comes from, what runs before main and why main is therefore called rather than first, and who receives the number main returns. It is the last piece of the picture Module 1 set out to draw, and with it you should be able to account for every stage between typing a character into an editor and reading a number back from a shell.
Foundation12 min readCPart IFundamentalsExecution
Part I · Module 1 · Chapter 1.7 · What Happens When a C Program Runs
1. A File Is Not a Process
You have a file. It is a few tens of kilobytes, it sits in a directory, and if you never type its name again it will sit there indefinitely, doing nothing whatsoever. Chapter 1.6 ended by driving a wedge into that observation. Here is the wedge, all the way in.
Your executable is a file. It sits on a disk. It has a size. You can copy it, rename it, email it, and leave it untouched for a year. While it is doing that, it is not doing anything at all — it is data, in the same way your .c file is data.
A running program is not a file. It is the machine actually carrying out work, with the contents of that file as its instructions. It has a current position in those instructions, memory it is using, and an amount of time it has been running for. When it finishes, that all goes away; the file remains, unchanged, ready to be run again.
One file, run three times, is three separate running programs. They do not know about each other, and none of them changes the file.
So "running a program" is not something that happens to a file. It is something that gets set up from a file, and the setting-up is what this chapter is about.
2. What Has to Happen Before Your Code Can Start
Between your typing ./hello and your main beginning, several things have to be arranged. You do not write any of them, and you will never see most of them, but knowing they exist explains several things that are otherwise arbitrary.
Step 2 answers the question Chapter 1.6 left hanging. Your executable did not contain printf; it recorded that it needed a library that provides it. This is the moment that gets resolved. As the program is loaded, the required library is located and made available, and only then can a call to printf go anywhere. If that library cannot be found, the program fails here — before a single line of your code has run — which is a fifth kind of failure, distinct from the four in Chapter 1.6's table, and one that looks baffling until you know this step exists.
Step 3 is the one worth dwelling on.
3. main Is Called, Not First
Here is the claim Chapter 1.4 made twice and deferred twice, now stated plainly:
mainis where your code begins. It is not the first thing the machine does.
Before main, code supplied by your implementation runs. It is usually called startup code, and it exists to put the machine into the state C programs are entitled to assume. It then calls main — as an ordinary call, the same shape as your call to printf in Chapter 1.4.
That single fact explains something that has been slightly odd since Chapter 1.4. Why does main return a value? Returning is what you do when someone called you. A thing that was simply the first instruction on the machine would have nobody to return to, and return 0 would be meaningless. It is meaningful precisely because main sits inside a call, and the value goes back to the caller.
What startup code actually does varies by implementation and platform, and this curriculum is not going to pretend otherwise. The portable statement is the one that matters: something runs before main, main is called, and your program's environment is prepared for you before your first statement executes.
4. What the Language Guarantees, and What Your System Arranges
This chapter is more platform-dependent than any before it, so it is worth separating clearly — using exactly the categories Chapter 1.3 gave you.
| Claim | Category |
|---|---|
main is called when the program starts | Defined by the standard |
Returning from main reports that value as the program's result | Defined by the standard |
0 conventionally means success | A convention, honoured everywhere, not a language rule |
Code runs before main to prepare the environment | Effectively universal, and the standard allows for it |
| How the file is loaded into memory | Your operating system's business, not C's |
| When and how a required library is located | The implementation's and system's business |
| What a non-zero status means | Whatever your program decides |
The first two rows are the ones you can rely on anywhere C runs. The rest is true of ordinary programs on ordinary systems, and Part II eventually meets software where several of those rows have entirely different answers — code with no operating system beneath it, where the startup arrangements are something an engineer writes rather than inherits.
5. Watching the Return Trip
Chapter 1.5 showed you $? without explaining what put a number there. Now it can be traced end to end. Take the smallest program that reports a failure:
int main(void)
{
return 3;
}Build and run it, then ask the shell what it got:
cc -std=c17 -Wall -Wextra -Wpedantic status.c -o status
./status
echo $?Output:
3Follow the 3. You wrote it in your source. The compiler carried it into the executable. When you ran the file, the system loaded it, startup code ran and called your main, your main returned 3 to that startup code, and the startup code reported it as the program's result. The shell then read it and echo printed it.
That chain crosses every stage in Module 1. It is the smallest complete demonstration that the stages are real and connected — a value you typed came back out the far end, having passed through all of them.
One detail: returning a value from main is defined to have the same effect as ending the program with that value by other means. Chapter 12.7 covers the other means and the conventions around choosing a number. Here, the point is only that return from main is not a special ritual — it is a return, to a real caller, whose result becomes the program's report.
6. When Running Fails and Building Did Not
Chapter 1.6's table had four rows. Running gives us the fifth and sixth, and they belong to different moments:
| Symptom | When | Cause |
|---|---|---|
| A complaint about a missing library, before any output | Loading, before main | The library the linker deferred could not be found on this machine |
| No output at all, or partial output, then the program stops | While running | Your code — Module 13's subject |
| The program runs and prints something wrong | While running | Your code was valid and said the wrong thing |
The first row is the one worth recognising now, because it is the only failure in Module 1 that happens after a completely successful build and before your program does anything. A build on one machine and a run on another is exactly when it appears. Nothing is wrong with your source, nothing is wrong with your executable, and the message will mention a file you have never typed — which makes sense only once you know that step 2 of Figure 1 exists.
7. What You Should Be Able to Say Now
- An executable is a file; a running program is the machine carrying out that file's instructions. Running one does not change the file, and the same file can be running several times at once.
- Before your code starts, the system loads the file's contents into memory and locates any library code the executable still requires.
- Code supplied by your implementation — startup code — runs before
mainand prepares the environment. mainis called by that code. It is not the first thing the machine does.- That is why
mainreturns a value: it has a caller, and the value becomes the program's reported result. 0meaning success is a convention, not a language rule. Thatmainis called, and that its return value is the program's result, are language rules.- A program can fail after a completely successful build and before running a single line of your code, if a required library cannot be found.
8. Exercise
Exercise 1 — separate file from process. You compile a program, then run it three times at once in three terminals. How many executables exist, how many running programs exist, and what has happened to the file? Answer in three short sentences.
Exercise 2 — explain the design. Using §3, explain why it makes sense for main to return a value, and what would be odd about return 0 if main really were the first thing the machine did.
Exercise 3 — classify the claims. Using the table in §4, say for each whether it is defined by the standard, a convention, or your system's business:
(a) main runs when the program starts.
(b) A program returning 0 indicates success.
(c) The executable's contents are copied into memory before running.
(d) A program returning 2 means a file was missing.
Exercise 4 — place the failure. A colleague builds a program successfully on their machine, copies just the executable to yours, and running it immediately fails with a message naming a library file — before any of the program's own output appears. Using Figure 1, say which step failed, and explain why recompiling your colleague's source on your machine would be a reasonable thing to try.
Exercise 5 — trace the whole module. A program's main contains only return 7;. Describe, in order, every stage that number passes through between you typing it into an editor and reading 7 from echo $?. Name at least five stages; Module 1 has taught them all.
9. Summary
- An executable is a file; a running program is not. The file is data at rest; running is the machine carrying out its instructions, and the file is unchanged by it.
- Loading comes first. The system brings the file's contents into memory and locates library code the executable only recorded a need for — which is where Chapter 1.6's missing
printffinally arrives. - Something runs before
main. Startup code supplied by your implementation prepares the environment, and then callsmain. mainis called, not first — which is exactly why it returns a value, and whyreturn 0is communication rather than ceremony.- Separate what C guarantees from what your system arranges. That
mainis called and that its return value is the program's result are language rules. Loading, startup details and the meaning of a non-zero status are not. - A program can fail between a successful build and its first statement, and knowing that loading exists is what makes that failure readable rather than baffling.
Module 1 in one sentence
You can now follow a C program from text you typed, through a stated language standard, a four-stage toolchain and a loaded image, to a running process and a number handed back — and you can say, at every step, which part is the language and which part is your tools.
What you cannot yet do is make a program hold anything. Every program in this module either printed fixed text or returned a fixed number; none of them kept a value, changed it, or worked anything out. That is the whole of Module 2, and it starts where C's real subject starts: what a value is, where it lives, and what it means to give it a name.
Standards & specifications
- Governing standard
- ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)
Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the C Programming curriculum.
