Programs go through a variety of stages on the way to execution. Depending on the type of language, some steps may be added or removed to the list, but here are the stages for a compiled language like C.
All programs start with a problem which needs to be solved. This problem could be as complex or simple as you want, but it must be broken down and understood before a proper program can be built.
If you are familiar with agile techniques, this is where we build our “user stories” – examples of what the user wants to do and see when the program is running.
In order to solve the problem, we first create an abstract solution, based on algorithms and interfaces. This abstract solution provides a basic solution for every piece of the problem, but it doesn’t yet cover any details or specifics. Programmers are known to create pseudocode, charts, flash-cards, and all manner of other devices for making the solutions more readily accessible.
In agile development, this is where we build “use cases” – breakdowns of how the user stories are made reality.
Abstract solutions are easy for us humans to understand, but not so easy for computers. We translate the abstract solutions into source code, written in our language of choice.
At this stage, we handle all the details required to make the abstract solutions specific. We define states, variables, functions, and objects at this point. This is also where we do all our debugging, which is not a trivial task for most programs.
This usually takes most of the program creation time.
Source code is written in human-readable language, which means that computers can’t handle it. The compiler handles the first half of the translation between human language and machine language.
A compiler is composed of two programs: the preprocessor and translator. The preprocessor reads the human language, removes comments, and orders the results. The translator takes the ordered result and translates it into machine language.
The translation stage creates object code, which is machine readable code. Unfortunately, we usually rely on libraries when designing our code. Libraries are batches of object code that someone else designed, which are associated with header files.
When we include a header file in our source, we introduce a list of symbolic links to other functions. These symbolic links cannot be executed, because there is no actual code inside them.
The linker connects object code files together with libraries to create executable code. It does this by replacing symbolic links with the actual library code, generating one complete package which the computer can read and run.
Once we have an executable program, we need to load it into the system to run. The operating system provides a loader program, which reads the executable program into memory and prepares resources (memory, etc.) for it.
Um…you run the program. Things happen.
Hopefully, it happened correctly, and you don’t have to go debug it.
When we’re done with the program, we put it away somewhere. We usually store it on a hard drive or removable medium so that we can look at it or use it later.
Of course, you could always delete the program, but you did do all that hard work.