To solve a problem with a computer, you have to understand how it works first.
Almost all computers that are in use today are digital. The analog computer is a thing of the past, so we can focus on digitial computers. The word digital in the context of computers mean that they based on binary numbers – numbers that consist only of zeroes and ones. This fact determines everything how a computer works. The amazing thing is that just with two numbers, computers can be used to solve a huge variety of tasks.
To understand how a computer achieves this feat, you have to learn to use binary numbers yourself. This is the first step towards thinking like a computer.
1. Binary Numbers
You need to become very proficient using binary numbers first. When you see a binary number, you need to be able to convert it into a decimal number in your head and the other way round.
Because binary numbers are so important (they are also very long when you write them down) programmers often use different representations of binary numbers. The most important are hexadecimal numbers. Their advantage is representing four bits in just one number. Four bits are a Nibble, and eight bits form a byte. So with just two hexadecimal numbers, you can write down the value of a byte.
As an example, instead of ‘1000’ you write ‘8’, instead of ‘10001111’ you write ‘8F’. Don’t understand it yet? Than start training hexadecimal numbers. You will encounter them so often during your work as a programmer that you need to be able to convert them to binary or decimal numbers in your head.
A binary number has a fixed lenght. The number of bits determines the maximum value that a binary number can have. A binary number with 8 bits can have 28 = 256 different values. This number rises exponentially with the number of bits. If you allow 4 bytes for a number, it has 4 * 8 = 32 bits and can store 232= 4.294.967.296 different values.
What kind of numbers you store in these bits is completely up to you. These bits can represent an integer value without any decimal places. They can also store a value with decimal places – but then the range is different. The number can also either have a sign or not. We will talk more about the different data types of binary numbers in a later chapter.
For now it is important to remember that the computer does everything with binary values. These values can become very large, but they still have a limit. You have to keep this in your mind all the time.
2. Boolean Algebra
Now you know about binary numbers. Then there is Boolean Algebra. This basically teaches you the operations that you can perform on two values, often called True and False. This has two very important implications:
First, computers use True and False, often called Boolean Values, to determine the next step in their calculation. We will cover that in Step 3.
Second, they allow computers to do calculations with binary numbers. Isn’t this something?! It works like this: Every bit in a binary number can be interpreted as a Boolean Value. We could say a zero means false and a one means _true. By applying the right operations from Boolean Algebra to the bits of two binary numbers one at a time, we can perform all kinds of mathematical operations like addition, subtraction, division and so on.
This is the topic of Computer Arithmetic. It is implemented in a specific part of processor of a computer. It is called the Arithmetic Logic Unit, or ALU.
3. Control Units
So one thing that computers can do is perform calculations on binary data. And, frankly said, that is actually all they do. They continuously take some kind of data, perform an operation on it and store the result somehwere else.
Just what kind of operations are performed on which data, this is the actual program of a computer. And it is executed by the computer’s Control Unit.
You can imagine the Control Unit like a clerk that has to perform a set of tasks during the day. He has a file cabinet full of files. Each of these files has a unique number. And each file has a content that is a digital value. The clerk has access to an ALU like the one that we described above. We can think of it as a calculator for binary numbers.
When the clerk starts work in the morning, he goes to a special part of this file cabinet and takes out a stack of cards that contain instructions. Then he takes the first card from the stack and looks at the instruction.
A typical instruction might look like this:
- Take file number 21 and note down its value.
- Take file number 423 and note down its value, too.
- Take these two numbers and put them into the ALU. Tell the ALU to add these two numbers.
- Take the result of this addition and write it down in file number 99.
When the clerk is done with this task, he will take the next card from his stack of instructions.
From time to time, the clerk will get a different kind of instruction. It might read like this:
- Take file number 55 and note down its value.
- Take file number 46 and note down its value.
- End this task and take a new instruction card, following these rules:
- If the content of file number 55 is greater than the content of file number 46, take the next card from the instruction stack.
- Otherwise, take the instruction card with number 1020.
Because the Control Unit can evaluate these conditions, a program can react on the values it finds in the file cabinet. When it performs such a branch, it takes a different path of execution from there on.
Let’s see where we stand now: A control unit together with an ALU form the CPU (Central Processing Unit) of the computer. The ALU can perform arithmetical operations on our binary data. And the Control Unit decides which operation to perform, based on a list of instructions. It can also decide which operation to perform, based on the state of some data. But where do all these values come from, and where to they go?
A computer stores all the data it needs in its memory. The memory is a storage for binary data. In our example with the clerk, the file cabinet is the memory.
The memory can hold both the instructions and the data. Both is just binary data.
Just as a file cabinet has a limited size, every memory has a limited capacity. The capacitiy is usually given in bytes, which are units of 8 bits.
To find a value in memory, the computer has to know how to find it. Therefore, every part of the memory has a unique address. It refers to one byte in the memory. The bigger the memory, the bigger the highest possible address. Because the addresses on computers can become very large, it is more efficient to write them down in hexadecimal numbers.
OK, but how does the computer know what is what? And how does it know if a memory position holds a number, a boolean value, a character or a more complex data type? Actually, it does not know this by itself. All memory looks the same to it.
When the computer stars, it will look at a fixed memory location and tries to execute the instruction it finds there. So we have to place a valid instruction at this memory location. If we don’t: Too bad for us! The computer won’t start.
If we do, the computer will execute it and then take the next instruction at the next location. The control unit takes track of the next command to execute with a special memory that is called the program counter. When a command is executed, the program counter is set to the address of the next command to execute. In normal program flow, it will just be the next memory address. But if the control unit executes a branch, the program counter can also be set to a completely different address. Then the program will continue from there on.
Each command that needs some data from the memory contains the address where to take the data from. It also states how many byte to take from that location. And it contains the memory address where to put the data to.
The control unit does not care if the addresses are correct. It does not know which kind of data is where. All the information about the memory to read from and to write to has to come from our program.
We programmers have to ensure that we read and write on the correct addresses. Take your time to understand the concept of memory in programs. This is how you avaid strange bugs, crashes of the computer or security problems in your product.
Types of Memory
A computer has access to different types of memory. They can be categorized in two dimensions: Speed and size. The fastest memory is the smallest, the biggest memory is the slowest.
The fastest memory is inside the registers. These are very close to the CPU. They can only store a few bytes, but they are very fast. If the program has to do some very fast calculations, they can be accelerated by putting the data in these registers.
There are some special registers that can’t be used directly by the programmer. One example is the program counter mentioned above. Each computer architecture can have a different set of special registers.
Then there is RAM, which is short for Random Access Memory. This is also very fast memory. Today’s computers typically have severyl gigabytes of it. RAM is also very fast, but slower than registers. It will lose its contents when the computer is powered off.
The slowest memory is the hard disk. It has high capacity and can store multiple terabyte of data. But reading and writing to and from it is several magnitudes slower compared to RAM. This is why most programs operate on data in RAM and only read from the hard disk when they need it.
A faster alternative today is flash memory which is used in smartphones and also many laptops and desktop computers. It is faster and more expensive than a hard disk, but still slower than RAM.
This is all well and good, you might say. But I don’t want the computer to just write some memory. I want it to show a picture, play a sound or send a message to my friends. How does it do that?
Well, you are right. A computer does a little more than just read and write from memory. But it does not know that! How is that?
The trick is memory mapped Input/Output. The CPU communicates with the computer’s peripheral devices using memory addresses. A peripheral device could be a display controller, a networking chip or a touchscreen controller. To receive input from the touchscreen, the CPU reads data from the memory address that is assigned to the touchscreen. It does not need to know that his is not memory. It simply reads the data from the address that is assigned to the touchscreen as if it were memory. The same goes for the output. To show something on the screen, the CPU just writes to the memory address of the display controller. That controller than takes this data to show an image on the screen.
With this trick, a CPU just has to know how to read from and write to memory addresses to perform all its task.
Of course, there are more aspects to the communication between the CPU and its peripherals, such as interrupts. But the most important concept is that of writing and reading to memory.
Why Do I Need to Know This?
While the basic operation of the CPU sounds quite simple, programming it is complicated. You have to find the correct instructions, have to know what data is at which memory location and put everything in the correct order.
Of course, you don’t have to do all of this on your own. A lot of work is already done for you by the BIOS and the operating system of your computer. They take care to put the first instructions for the startup of the computer at the correct memory location. They also configure the peripherals and manage the computer’s ressources. Of course, a modern computer also runs more than one program at the same time. This is also coordinated by the operating system.
And with modern programming languages and runtimes, you don’t have to care where to put the program counter or at which memory location a variable is.
So you might think: Do I really have to learn all of these?
Yes, you do! Even if all these processes are hidden from you, you might still stumble into a problem that you cannot understand without this knowledge. If your program crashes, the computer might present you with a report full of hexadecimal numbers. How else could you interpret this?
Or a hacker attacks your program running on a web server to spread malware. How did he do it? Without understanding the concept of computer memory, you have no chance to prevent a buffer overflow.
And even today, there are many programming task that require dealing with the processor directly. When you develop software for an embedded system, you will not have all the services that Windows or iOS provide to the programmer. You will write to memory addresses directly. If you do something wrong, you can crash the whole system. You might even risk the safety of your customer. So you better know what you are doing.
To be able to program a computer, you need to understand his inner workings. This section gave you an overview about the basis of digital computers. It is actually very simple:
- Read the instruction and data from memory.
- Perform a calculation.
- Store the result at another address.
With just these three steps, computers can do all the wonders that we are used to today.
If you learn these basics thoroughly, it will make the next steps on your path to becoming a professional programmer much easier.
In the next section, we will learn how to reshape problems in a way that a computer can understand them.