1. Introduction
How can a computer understand our code written in a language close to the human language? For example, can we write our code using 0’s and 1’s since we have always heard that this is how a computer operates?
Both of these answers are related to the level of abstraction of a programming language.
In this tutorial, we’ll describe the differences between high-level and low-level languages. In the end, we’ll be able to identify the main advantages and differences between these two types and understand why they are equally relevant.
2. Levels of Abstraction
Although we can arrange programming languages accordingly to their level of abstraction, we should know that this is a general rule.
Depending on the reference, there can be even a third category called Mid-Level or Hybrid level of abstraction. But the important part is that all of them consider how close is the language structure and syntax to the human language and how far it is from a sequence of bits that a computer can understand:
2.1. Low-Level
We classify a programming language as a low-level language if its level of abstraction makes it harder for us, humans, to understand what is written by just looking at the code. For us to program at this level, we should know where the code will be executed since the final code won’t be portable. Besides that, it is recommended to have previous knowledge and experience in programming.
The main advantage of writing a low-level code is knowing precisely which operation is being executed by the computer.
When we have strict performance requirements, optimization concerns, or limited resources, we should consider choosing this type of programming language. As examples of applications using low-level languages, we have operating systems, embedded systems developed to run in microcontrollers, and portable electronic devices.
Within the low-level languages, we have two categories: Machine Code and Assembly.
Machine Code is the sequence of 0’s and 1’s that we mentioned at the beginning. It is not intuitive to look at one concatenation of digits and understand the programming logic behind that.
For this reason, we have Assembly language. Although it is still far from the human language, it can be understood after putting some effort to learn to use a set of instructions. A source code written in Assembly will require an assembler to convert it to Machine Code.
2.2. High-Level
On the other hand, we have high-level languages. Therefore, when we use programming languages located at this level, our code will be much closer to the human language.
The main advantage is that we’ll be able to write complex programs fast using structures and syntaxes developed to facilitate our job.
We’ll need a compiler or an interpreter, depending on the programming language, to convert our code to Machine Code. The benefit of this strategy is to have a portable code at the end that can run on different computers with the same operating system.
3. Example
To illustrate the difference between coding using a low or high-level language, we’ll show a simple code that calculates the average of numbers and and prints the result on the screen.
We can write this simple program using C. Although C does not have one of the highest levels of abstraction such as Python, it is not a low-level language:
#include <stdio.h>
int main()
{
int num1, num2;
float avg;
num1 = 2;
num2 = 8;
avg= (float)(num1+num2)/2;
printf("Average of %d and %d is: %.2f",num1,num2,avg);
return 0;
}
The second code was created using ARM64 GCC 5.4, a low-level assembly language designed for specific hardware:
.LC0:
.string "Average of %d and %d is: %.2f"
main:
stp x29, x30, [sp, -32]!
add x29, sp, 0
mov w0, 2
str w0, [x29, 28]
mov w0, 8
str w0, [x29, 24]
ldr w1, [x29, 28]
ldr w0, [x29, 24]
add w0, w1, w0
scvtf s1, w0
fmov s0, 2.0e+0
fdiv s0, s1, s0
str s0, [x29, 20]
ldr s0, [x29, 20]
fcvt d0, s0
adrp x0, .LC0
add x0, x0, :lo12:.LC0
ldr w2, [x29, 24]
ldr w1, [x29, 28]
bl printf
mov w0, 0
ldp x29, x30, [sp], 32
ret
We can easily see that the Assembly code has more lines than the C code. But more important than this, to understand what each line of code is doing, we need an extensive analysis of each instruction, keeping track of the values on each register.
To implement a loop, a conditional statement, and to plan a workflow, our code is entirely different depending on the level of abstraction that we are using.
4. Applications
It is hard to define the hardest level of abstraction or the most important one. However, we can say that high-level languages are more popular since they have more applications and versatility.
We can use high-level languages to develop websites, mobile apps, databases, or software using Artificial Intelligence algorithms. If we had to do this in Assembly or Machine Code, without the help of frameworks and their level of abstraction, we would certainly spend much more time and might not even reach the desired result.
But low-level languages are widely used in the software industry with some conventions defined by Microsoft, for example.
Although we might think that computational power is no longer a limitation, there are some environments such as a production line in which we won’t have a PC running a friendly operating system with several GBs of memory.
Instead, we’ll have microcontrollers with well-defined tasks and limited storage and processing capability. And in this case, each operation should be carefully analyzed to detect any unnecessary redundancies or extra calculations.
5. Conclusion
As a take-away from this article, before choosing a programming language, we should first decide which path we intend to follow in our careers. Both levels of abstraction can be learned, but we need to keep in mind that for low-level programming languages, the number of applications is more limited.