Chapter 1: Compiled vs. Interpreted Programming Languages

Chapter 1: Compiled vs. Interpreted Programming Languages#

Machine Code & Assembly#

A computer’s CPU only understands a very limited set of basic instructions which are called machine code and it is written in binary. For instance, we could have the following instruction: 10110000 01100001. However, as is immediately evident, machine code is hard for us to read and understand. For this reason, people invented assembly language, where each instruction is defined by short abbreviations, and names and numbers can be used. For instance, the previously mentioned instruction can be written in assembly language as: mov al, 061h. The only problem is that a computer cannot directly understand this, and instead must have an assembler translate it into machine code. However, there is one issue with both machine code and assembly: each CPU has a different set of instructions and thus neither machine code or assembly is very portable to other CPU architectures.

High-level languages#

Portability issues with machine & assembly are solved* with high-level languages such as C++, Python, Javascript, etc. These are designed to be very human readable and to be used in any kind of computer, regardless of the CPU’s architecture. These types of languages can be divided into two main categories:

  • Interpreted

    • E.g.: Python

    • There is a program called an interpreter which directly executes the instructions in the code without compilation.

    • Highly portable, but very inefficient.

  • Compiled

    • E.g.: C/C++

    • There is a program called a compiler which translates the source code into another, lower level language (e.g. assembly, machine code).

    • The compiled code is very efficienct compared to its interpreted counterpart.

Higher-level languages makes programming far more accessible to a wider public. Remember the cryptic snippet of code in machine code and assembly seen in the previous section? Here is the exact same instruction, but in C++: a = 97;. You do not need to be a programmer to understand what that those!

As an example of how compilers work, we can visit godbolt.org to see the end result of a compilation! Just copy the following code into the cell on the left, choose your favorite compiler, and see the results on the cell to the right. We will discuss more about what code such as this does later on.

#include <iostream> 
// This is a comment!

int main() {
    int n = 3;
    int m = 5;
    int sum = n + m;
    return 0;
}

Compiling C++ Source Code#

_images/mermaid-diagram-2024-06-05-001615.svg

Fig. 1 Building process flow chart#

Compilation of source code is only the first step in the construction of executable software from source code. We can summarize the whole building process as follows:

  • The compiler checks that the input C++ code is well written and does not have any syntactical errors.

  • The compiler translates the C++ code into machine language instructions. This produces an object file (.obj or .o) for each input file.

  • The linker combines all of the object files, ensuring all cross-file dependencies are resolved properly, and linking library files. This almost always includes the C++ Standard Library which provides some basic libraries such as the iostream library.

  • You finally have an executable file you can run.

Exercise 1.1

Let’s compile our first C++ code. First open helloworld.cpp with your favorite text editor (can you guess what it does)? Next, run g++ helloworld.cpp -o helloworld.o. This will produce a new file called helloworld.o. This is the output of the compilation process. To run it just run ./helloworld.o in the terminal.