What makes C++ a low-level programming language?
Posted: June 27th, 2019, 11:11 am
Most people consider C++ as a low-level programming language. Compared to higher level languages like Java and Python, C++ works on a much lower level than these languages would allow. The majority will agree with this statement but few of them could clarify what that actually means. This begs the question “What even is low-level programming anyway?” To answer this, I will show you what low-level programming looks like through the eyes of C++.
First, let’s start with C++’s memory model. This is memory:
01001000011001010110110001101100011011110010000001110111011011110111001001101100011001000010000100000000000000000000000000000000
An array of 1s and 0s called bits that are stored somewhere on your computer. These bits are divided into bytes where each byte has a unique address that state where it is in memory.
These bytes are used to contain encoded information in the form of objects
This is what C++ calls an object. It is 16 bytes big and starts at the address 0x15. This object represents the sentence “Hello world!” where each character in the sentence is encoded into 1s and 0s. The first 12 bytes contain the 12 encoded characters while the remaining 4 simply contain the encoded value “nothing”. Computer scientists commonly refer to this nothing value as null.
Objects can represent all sorts of things from images and sound to the types of food available for purchase from a shop. Software and hardware alike manipulate these objects to function and communicate with each other. Here’s one example:
When a keyboard connects to the computer, it’ll create a key buffer object in its memory. This particular key buffer in the picture is 10 bytes big. The keyboard will set each byte to null at first and as keys are pressed on it, it’ll look through the buffer object to find an empty byte to store the key into.
When a piece of software wants to get input from the keyboard, it simply needs to go to its key buffer object and search the buffer for non-null bytes. When it finds one, it stores it in its own personal buffer for later use. Before it leaves, it empties the byte by setting it to null allowing the keyboard to write to it again.
The software can use a similar process to draw images to the monitor.
If you look at a monitor with a magnifying glass, you can see little lightbulbs on its surface arranged in a grid. When enough of these lightbulbs come together in different colours, it’ll be able to create all sorts of complex images. An image is really just a series of dots of different colours called pixels. When it’s time to draw a new image to the screen, the monitor will look at its pixel buffer object, goes through each pixel in the buffer one by one and sets its lightbulb to show that pixel’s colour. If a piece of software wants an image to be shown on the monitor, it’ll simply copy that image’s pixels into this pixel buffer.
Ordering the computer to execute an action like shutting itself down can be done by setting bytes too. In the computer’s memory, it’ll contain a special object that’s just the size of one byte. Set this byte to a specific value that means “shutdown” and the computer will detect it instantly. Once detected, it’ll send a signal to the power supply to cut out the power turning the computer off.
Now these are vastly oversimplified explanations of how the hardware actually interacts with the software but it helps to prove a point: low-level programming is all about manipulating memory. Everything you want to do with a computer; you do it by moving memory from one place to another. Playing sound on the speakers, drawing images onto the monitor, getting keyboard input and even turning the computer off can be achieved by writing the right bits to the right bytes. A good piece of software is one that can move memory in the most efficient and safest way possible to achieve its goals.
This is why C++ is considered a low-level programming language. It gives you the freedom and the tools you need to manipulate memory however you wish. Languages like Java and Python actively block you from accessing memory directly. They personally do the memory management for you and make you use magic functions(print(), open()) and other special keywords to set the memory on your behalf. C++ doesn’t give a shit. You give C++ the address of any byte you want and you can change it down to its individual bits.
With this much power though, it’s very easy to shoot yourself in the foot. A few bad writes to certain bytes are all it takes to cause the computer to malfunction and turn into a very expensive brick. However, low-level programming doesn’t have to be this dangerous endeavour that everyone makes it out to be. By following certain programming guidelines and principles, you can do low-level programming with the same level of safety and simplicity that high-level programming languages provide without sacrificing much of the freedom that it gives you.
In my next thread, I’ll be explaining what these guidelines and principles are but until then, try to do your own research on how computer memory works and how it interacts with the various kinds of hardware.
First, let’s start with C++’s memory model. This is memory:
01001000011001010110110001101100011011110010000001110111011011110111001001101100011001000010000100000000000000000000000000000000
An array of 1s and 0s called bits that are stored somewhere on your computer. These bits are divided into bytes where each byte has a unique address that state where it is in memory.
These bytes are used to contain encoded information in the form of objects
This is what C++ calls an object. It is 16 bytes big and starts at the address 0x15. This object represents the sentence “Hello world!” where each character in the sentence is encoded into 1s and 0s. The first 12 bytes contain the 12 encoded characters while the remaining 4 simply contain the encoded value “nothing”. Computer scientists commonly refer to this nothing value as null.
Objects can represent all sorts of things from images and sound to the types of food available for purchase from a shop. Software and hardware alike manipulate these objects to function and communicate with each other. Here’s one example:
When a keyboard connects to the computer, it’ll create a key buffer object in its memory. This particular key buffer in the picture is 10 bytes big. The keyboard will set each byte to null at first and as keys are pressed on it, it’ll look through the buffer object to find an empty byte to store the key into.
When a piece of software wants to get input from the keyboard, it simply needs to go to its key buffer object and search the buffer for non-null bytes. When it finds one, it stores it in its own personal buffer for later use. Before it leaves, it empties the byte by setting it to null allowing the keyboard to write to it again.
The software can use a similar process to draw images to the monitor.
If you look at a monitor with a magnifying glass, you can see little lightbulbs on its surface arranged in a grid. When enough of these lightbulbs come together in different colours, it’ll be able to create all sorts of complex images. An image is really just a series of dots of different colours called pixels. When it’s time to draw a new image to the screen, the monitor will look at its pixel buffer object, goes through each pixel in the buffer one by one and sets its lightbulb to show that pixel’s colour. If a piece of software wants an image to be shown on the monitor, it’ll simply copy that image’s pixels into this pixel buffer.
Ordering the computer to execute an action like shutting itself down can be done by setting bytes too. In the computer’s memory, it’ll contain a special object that’s just the size of one byte. Set this byte to a specific value that means “shutdown” and the computer will detect it instantly. Once detected, it’ll send a signal to the power supply to cut out the power turning the computer off.
Now these are vastly oversimplified explanations of how the hardware actually interacts with the software but it helps to prove a point: low-level programming is all about manipulating memory. Everything you want to do with a computer; you do it by moving memory from one place to another. Playing sound on the speakers, drawing images onto the monitor, getting keyboard input and even turning the computer off can be achieved by writing the right bits to the right bytes. A good piece of software is one that can move memory in the most efficient and safest way possible to achieve its goals.
This is why C++ is considered a low-level programming language. It gives you the freedom and the tools you need to manipulate memory however you wish. Languages like Java and Python actively block you from accessing memory directly. They personally do the memory management for you and make you use magic functions(print(), open()) and other special keywords to set the memory on your behalf. C++ doesn’t give a shit. You give C++ the address of any byte you want and you can change it down to its individual bits.
With this much power though, it’s very easy to shoot yourself in the foot. A few bad writes to certain bytes are all it takes to cause the computer to malfunction and turn into a very expensive brick. However, low-level programming doesn’t have to be this dangerous endeavour that everyone makes it out to be. By following certain programming guidelines and principles, you can do low-level programming with the same level of safety and simplicity that high-level programming languages provide without sacrificing much of the freedom that it gives you.
In my next thread, I’ll be explaining what these guidelines and principles are but until then, try to do your own research on how computer memory works and how it interacts with the various kinds of hardware.