Contents
Computers are found in almost every line of work and it almost all facets of life today. Being informed on how computers work and how to use a computer has become an almost critical life skill. The ability to think computationally is a skill that extends beyond simply programming a computer but also to many other aspects of life today. It is for these reasons, that learning to interact with the computer in a more advanced way than simply using a computer becomes a critical skill.
As we journey from novice software developers to more advanced programmers, we will explore many different avenues to become proficient in explaining to the computer what we want it to do. First, we will examine how the basic parts of a computer are connected. This is not intended to be an in detail examination of a modern computer but rather an abstraction of a computer and how it works. It will be helpful when we write software to keep this model in mind.
Once we have a basic understanding of a computer and how it works, we will then turn our attention to the 4 basic ways we will structure our software. In brief, they are sequence, selection, iteration and subprogram or functions. We will see how each of these techniques allows us to craft a solution to our problem using simple ideas to build up a larger solution.
After we have completed our examination of basic techniques for structuring our code, we will then explore how we can extend these ideas and better model the data, or information, that our programs need to solve our problems. Along the way, we will also see how we can get data into and out of our programs using both the keyboard and monitor and files.
When we have completed this tour, we will have a fairly good grounding in the basics of programming and be set to take off into the larger world of C++. C++ is like an ice berg; most of the it lies below the surface. The 10% that is above the water comprises the majority of this book. Before we are done, we will take a few tentative dips below the water line and see some of the complexity that allows for the software that we use daily to work.
The computer is a complex machine that has many parts and subsystems that work together to make a complete system. The basic parts of a computer are, the central processing unit (CPU), the input output (I/O) system, memory and storage. Each of these pieces works on doing one job and doing just that job. When all of the pieces work together we can get wonderful combinations that allow us to do our jobs quickly and easily.
The CPU's job is to process the instructions for a program. The CPU only understands zero's and one's and so our programs will need to stored this way. This set of zero's and one's is called a binary program. While it is possible to write programs directly in binary, we will not be doing this. We will write our software using a higher level language and then converting it to binary. The compiler will be our tool for doing this.
The CPU typically is comprised of two parts, the ALU, or Arithmetic Logic Unit, that handles all of the computations and logical operations the CPU needs to do and the Control Unit that handles controlling the rest of the computer. A computer is nothing more than a machine that knows how to do mathematical operations and logical operations. So the ALU is essential to the CPU in getting the computer's job done. The control unit is needed to handle all of the other operations needed for the use of the computer - fetching and decoding instructions, selecting drives, handling input and output, etc.
The Input/Output or I/O subsystem is the set of components that allow the computer to communicate with the outside world. Typical input devices are keyboard, mouse, touch pad, touch screen, etc. Typical output devices include screen, printer, network connections, etc. Without being able to get information into and out of your computer, your general purpose computer would be not as useful. There are computers that are specialized and need very little to no input and have very little to no output.
The last main system in the computer is memory and storage. Every computer needs memory that it can use to do its work. There are very fast registers that are actually on the CPU. The CPU uses these registers for its own purposes. There is main memory that can be as large as 16 or 32 GBs. Finally there is long term storage. This storage might be in the form of spinning hard disks or solid state drives. These drives can be anywhere from a few hundred MBs to over 1TB in size.
All of these various pieces are parts are connected by a bus. The bus is where the information, the one's and zero's, travel to allow the computer to do its job. So while the computer is a complex machine it is made up from a series of smaller components, each one designed to do one job and do it well. We will follow a similar pattern when we design software. We will build up complex interactions from smaller pieces. Each piece will be designed to do one part and do it well.
So, in order to get our computer to do tasks for us, we need away to communicate with it. Each computer processor understands its own set of instructions. This set of instructions is encoded in binary. We can write our program in this language but it can be difficult to get correct for any program that is longer than a few statements. So one of the innovations programmers did was to invent a way of taking word like phrases and turning those into binary. These word like phrases are called assembly language.
Assembly language is also tied tightly to the processor. Each processor will understand its own set of assembly. It is easier to write assembly language code but it can still have the same issues as writing binary code. It can be difficult to get correct for a program that is longer than a few statements. Also, there are many issues the programmer has to deal with themselves, e.g. where variables are in memory, how large a variable is, etc. Programmers need a better way.
Binary language and assembly languages are two low level programming languages. They are called low level because the language is very tightly bound with the processor for which you are writing the code. There are hundreds of different processors out there today and if you have to rewrite your code for each one, you'd never get anything done. So programmers came up with higher level languages. Languages like Visual Basic, C, C++ and Java are higher level languages. The purpose of these languages is to allow the programmer to express their program in terms of the language. Ideally, this program can be written once, and then converted to assembly and binary code for each different processor on which the software is to be run.
The process of taking a program written in a higher level language can take a few different can be done in at least two different ways. You can interpret the instructions as the program is running. Languages like Visual Basic and scripting languages are interpreted. Interpreted languages have some advantages that allow for some advanced features of programming, like dynamic scoping and reflection. They have a disadvantage of typically being slower to execute because each statement in an interpreted language must be changed from high level language to machine code each time the program is executed. If we instead do this interpretation once, then we have a compiled language.
A compiled language, like C or C++, uses two different basic steps to take high level language and turn it into machine executable code. The first step is to use a compiler. The compiler takes as input the code the programmer has written and turns the code into object code. This is a binary format that is close, but not quite, what the computer wants. The second step is to use a linker. The linker takes the object files and turns them into executable code. The executable is also in binary form and is what the computer wants to execute. The advantage to the compiled program is that once these steps are taken, the program can be executed multiple times without having to do those steps again. Of course, if the program code changes, then those steps need to be done again.
For the most part, when a person sits down to write a computer program, they are trying to solve some problem. The problem could be almost anything - how to compute the area of a circle, how to route trucks to deliver packages, how to enter text to write a book, or even how to take source code and turn it into machine executable code. These examples are just the beginning of things people want to do with computers. It's clear that if we attempted to write code with out a clear process for doing so, we would be in trouble pretty quickly. So to tackle the problem of how can we write software in a manner that produces useful results, we will briefly examine a problem solving process.
George Polya was a Hungarian mathematician who spent a lot of his career studying just this problem; how to solve problems. In his context, the problems were mathematical, but we can adapt his four steps to writing computer programs. His four steps are as follows:
So we can adapt this to the following steps:
Understanding the problem is a fundamental step that is often overlooked. If you don't know what it is that you are trying to do, then you are very likely to solve the wrong problem. This is almost as bad as not doing anything. You should spend a significant amount of time making sure that you know what you are being asked to do. Ask questions when you are trying to understand the problem. Make sure that all points are clear. The better your understanding, the better your solution will be.
Once you think you have a clear understanding of the problem, you should begin to devise your solution. This plan can be written in plain language on paper. It might be an outline or a flow chart. It could be a set of index cards with major steps listed. The important part of this step is that you may not even be in front of your computer. Too many programmers rush to sit down and start pounding out code. Take your time with the first two steps and the last two steps will take considerable less time.
After you have a plan, now sit down and start writing code. If you have a clear plan, this step can be quite short. Once you understand how to take your natural language and convert it to a computer language this step can be easy; however without a clear understanding of the problem and a good plan in place, this step can flounder even the best and brightest.
The final step is of course to test your program. Look for edge cases and cases where different choices are made. There are tools that can help in testing and can help your determine how much of your code is run. Exhaustive testing is almost always not possible; however thorough testing can be done with a reasonable amount of effort.
These steps are not meant to be followed in lock step; one step flowing into the next with no hope for return. If during the planning step you realize that you don't understand something, then of course go and figure that out. If when you are implementing you realize your plan is lacking detail, go back and figure that detail out. That might even mean you need more information. If during testing, bugs are found, go back and fix them.
Written by: David McPherson © 2019