Programming with assembly languages is cumbersome, as we have seen in the previous chapter. For this reason, software developers write their code in high-level programming languages. A compiler then translates the program of the high-level language to the machine language. In this process, it handles many of the inevitable troubles of programming in assembly languages:
Assembly programs are not portable. They are always written in the instruction set of a processor family. To run the program on a different processor, it needs to be rewritten entirely. A program written in a high-level language, if there is a compiler for the new processor, can just be re-compiled.
Memory cells in assembly programming are unnamed and can only be accessed by number. The resulting programs are hard to read and understand for programmers. Furthermore, we need to decide which values are held in registers and when they are written to memory. This is particularly problematic if there are not enough registers available and when calling conventions need to be implemented.
Assembly languages provide little syntactical structure. Convenient concepts like the infix notation of mathematical expressions cannot be used in assembly languages. Complex expressions need to be serialized by hand and the intermediate results need to be stored to registers.
Control structures like “Do ... until a condition is satisfied” or “if a condition is fulfilled, do \(A\text{,}\) otherwise do \(B\)” are cumbersome to implement with branching instructions.
In this chapter, we study the programming language C,which remedies all of the above points. C provides abstractions to the programmer that hide the specifics of the underlying machines. As C programmers expect a translation to efficient machine code, C only provides as much abstraction as necessary to write portable programs without causing overhead. For this reason, some people refer to C as a “high-level assembler”. This intention gives C problematic properties that we will discuss later in this chapter (Section 4.15). These properties cause many severe security vulnerabilities in software written in C.
C is a very successful programming language. Every modern operating system is, because of C's close relation to the machine language, written in C. Software for which memory consumption and execution time are important are written in C. C does not need a complex run-time environment and is rather easy to translate into machine language. This renders C the default language when developing embedded systems. Moreover, C had a strong influence on many other successful languages like C++, C#, Java, JavaScript, etc.).