Register machines in C++

Here we explain a little about what C++ tries to do and how to convert a register machine program to C++.

1. About C++

C++ is a programming language. That is, it is a precisely defined language that can be used to write a program for a "machine" to carry out a specific computation.

The specific kind of "machine" that C++ is for is like that of a register machine with registers or variables, but many new kinds of variables are possible, for example containing different kinds of numbers or even other kinds of data. There is more on that later.

The commands INCR(x), DECR(x) etc. that we have been using are not part of the C++ language. They were defined as special "macros" in C++ (similar to "macros" in LaTeX defined using \newcommand or \def). As a general rule, the use of "macros" in C++ is considered poor style and to be avoided unless there really isn't any alternative (as was the case here). C++ is such a good language that there is almost never any need to use macros.

Every computer language has its strengths and weaknesses. C++ is a very good language for many different kinds of tasks. In particular it provides a large number of different kinds of constructs for many different programming styles. It aims to help the programmer write programs that are short and succinct. And it aims to help the programmer write programs that work at the very fastest possible speed. This makes C++ an excellent language for numerical work in mathematics. Other popular languages such as java and python are much slower than C++.

This comes at a price. To make C++ the best possible language for experts, a huge number of possibilities are provided, and the definitions of what they all do are all precise and technical. This means, unfortunately, that C++ comes with many traps and pitfalls for the unsuspecting beginner. The advantages of learning C++, however, outweigh the disadvantages: it is an excellent, fast, industry-standard language. It is well worth getting to know well.

All the traps and pitfalls will be clearly highlighted in these web pages. What you have to do is make sure that you avoid them. This requires paying attention to detail.

Generally speaking this module requires you to work harder at the beginning but if you can work like this you will have much more time and fewer pressures later on.

Sorry to say, but this is what this subject can be like: you can have a non-working program worth zero marks from one single symbol being incorrectly typed. Some people take this as a challenge! It certainly can be one, and tracking down an error can be fun (or frustrating, or both). Errors are part of the subject and testing carefully is part of it as well. This module is not about some theoretical subject in which errors don't ever happen. It's very much about those errors and how to find them when you have made them.

2. Converting register machines

This section is your first introduction to C++ syntax. C++ can be very unforgiving and details are very important.

The name C++ gives to registers is variables. Variables are not like variables in maths (because they can change). They are exactly like registers in register machines. We will use the word "variable" throughout from now on.

2.1. USES

In the register machines you had to say which variables you were using with "USES(x)" or "INPUT(x)". C++ is exactly the same.

The equivalent to "USES(x)" in C++ is

  int x=0;

(Type this exactly. Note that the case of the letters and the semicolon are important.) This declares a new variable called "x" which contains a value of type "int" and initialises it to zero.

"int" is like "integer" in mathematics, but you will already have realised that C++'s "int" is very limited, e.g. not all integers can be so-represented.

Variables can have any name, which is a combination of letters a-z, A-Z, digits 0-9, and _. Note that the space character, comma, fullstop, etc., are not allowed in a variable name, and upper and lower case are considered different. The first character must be a letter or the underscore, but normally you avoid having variables starting with underscore except for special tasks.

Pitfall. Be careful with semicolons! For example, "int x=0" without a semicolon will not work.

There are complicated rules for when you can and can't have two variables with the same name. We will come to this later. But for now remember that if C++ allows you to write int x in one place and int x again later on then the second delaration is a new variable and prevents the old variable being accessible.

Pitfall. Note that whenever you use "int" followed by a variable you are declaring a new variable, and that might not be what you want. Do not get into the habit of putting lots of "int"s about your program to avoid compiler error messages, without thinking about what you are doing carefully. This rarely works because you will have too many variables with the same name!

C++ variables of type "int" can have negative values as well as positive values.

2.2. INPUT and PRINT

In the machine model programs we have been doing, "INPUT(x)" is equivalent to declaring the variable, and getting its value. In C++ you write this as follows.

  int x=0;
  cout << "x? ";
  cin >> x;

The first line here is the declaration and initialisation we have already seen. The =0 here is optional but recommended.

The second line prints the message "x? " to the terminal (console output, or cout). You can put any message you like here, or even several such messages in separate cout lines. If you want to print a new line (carriage return) you do cout << endl;.

The third line gets a value from the terminal (console input, or cin) and puts it in x. There are special rules as to what happens if the user doesn't type a recognised number, and at some stage you might need to experiment with these. But for now just remember what you are supposed to type and do it correctly :)

Similarly, "PRINT(x)" is equivalent to the following C++ code.

  cout << "x = " << x << endl;

This prints the text "x = " without the quotes, followed by the value of x, followed by a carriage return. Note that things you print with cout << can be combined in one line.

Note. All of these require you to have written "using namespace std;" somewhere in your program so C++ knows what you mean by cout cin and endl. This is an option that is mainly useful for beginners only, and would not be recommended for large programs, but will be OK throughout this module. We come back to this later in this page.

2.3. INCR ZERO and DECR

There are two ways of getting C++ to do INCR(x). The first is just like the name of C++ and you type

  x++;

This makes x one larger. (It has the strange effect if x is already the largest possible "int" of making x become the smallest negative "int".) The other way of writing this is,

  x = x + 1;

Important. The symbol = in C++ does not mean "equals". It means "calculate the RHS and put its value into the LHS".

For ZERO(x) you just write

  x = 0;

For DECR(x), you can write

  x--;

or

  x = x - 1;

These make x one smaller, possibly negative. It also does weird things if x is already the smallest negative "int" value.

Pitfall. Be careful with semicolons! For example, "x = x + 1" without a semicolon will not work.

Pitfall. The symbol = is one of the most useful ones of all in C++. Not being fully aware of what = actually means in C++ is a major source of errors, especially for beginners (but not only beginners). Make a start in learning this carefully now.

2.4. IF and ELSE

To test to see if a variable x is positive you can do

  if (x>0) {
    // stuff to do if x is positive
  }

There are a number of variations of this, such as using < and using "else" (in lower case). For example,

  if (x<0) {
    // stuff to do if x is negative
  } else {
    // stuff to do if x is nonnegative
  }

If you want to test if x equals any particular value you use == as in

  if (x==1) {
    // stuff to do if x is equal to 1
  } else {
    // stuff to do if x is not equal to one
  }

If you want to test if x is not equal to any particular value you use != as in

  if (x!=10) {
    // stuff to do if x is not equal to 10
  } 

Pitfall. Do not use = when you mean to use ==. Your program might compile and give the wrong results.

Pitfall. Do not combine clauses in an "if" statement with "&" or "|" (for "and" and "or") unless you really are an expert. Your program might compile and give the wrong results. With care you can combine clauses using "&&" or "||".

In C++, the symbols & and | do not mean "and" and "or" in the way you expect. They are operators, but not useful ones for beginners. The operators "&&" and "||" are more useful to beginners, and we will look at these later.

Pitfall. C++ will allow you, in special cases, to omit the curly brackets. Do not do this until you know the relevant rules. In general it is good style to always use curly brackets as above.

Pitfall. C++ will allow you to put a semicolon in a number of other places but it is likely to be incorrect. For example, "if (x!=10); { x++; }" will compile but will not do what you want.

2.5. LOOP INFLOOP and BREAK

The direct translation of LOOP(x) { ... } is as follows.

  for (int i=x; i>0; i--) {
    ...
  }

This is slightly tricky, and for now you just type it exactly as printed, normally making sure that the variable i that is declared here is a new one you haven't used before. There are other variations of the "for" loop that we will look at later.

Pitfall. Be careful with semicolons! For example, "for (int i=x; i>0; i--); { ... }" will not do what you want.

Pitfall. Be careful with semicolons! For example, "for (int i=x, i>0, i--) { ... }" will not do what you want.

We will talk about other ways to use "for" in C++ later.

The direct translation of INFLOOP { ... } is as follows.

  while (true) {
    ...
  }

Pitfall. Be careful with semicolons! For example, "while (true); { ... }" will not do what you want.

We will talk about other ways to use "while" in C++ later.

"BREAK" translates to "break;" (including the semicolon). The direct translation of BREAKIFZ(x) is therefore as follows.

  if (x==0) { break; }

And the direct translation of BREAKIFNZ(x) is as follows.

  if (x!=0) { break; }

The "break;" statement is almost always found in an "if" statement like this and breaks out of the closest enclosing "for" or "while" loop. Of course there are other possibilities for the condition in the "if" statement like those given above, and the use of "else".

2.6. MAIN and END

For the main program, what I suggest for this module is that you use the following equivalent to "MAIN ... END" in C++.

#include <iostream>
using namespace std;

int main() {

  // stuff between MAIN and END goes here

  return 0;
}

Note that you type "#include <iostream>" instead of including some other set of macros. (The <iostream> defines cout and cin and a number of other things you sometimes need such as endl.) I've also put the "using namespace std;" in here.

Pitfall. For more advanced programs, exactly what #include statements you have, and what order they are in matters a lot. So does your using namespace statements.

You may get away with omitting the return 0; but I do not recommend it. It is necessary so your program communicates properly with the operating system to say it completed successfully.

Example.

Here is the full multiplication example we saw before, translated into C++ style.

#include <iostream>
using namespace std;

int main() {

  int x=0; 
  cout << "x" << "? "; 
  cin >> x; 

  int y=0; 
  cout << "y" << "? "; 
  cin >> y;

  int z=0;

  for (int i=x; i>0; i--) { 
    for (int j=y; j>0; j--) { 
      z++; 
    } 
  }

  cout << "z" << " = " << z << endl;

  return 0; 

}

Exercise.

Take one of the example register machine programs from earlier, or one you wrote yourself, and convert it into C++ style. Compile it and run it as before.

3. Summary

You have actually got quite a lot of useful C++ knowledge now and can write interesting C++ programs, especially when you know (as you will surely have guessed) that + means "plus", - means "minus" and * means "times", etc., and hence can write things like x = y + z; meaning "calculate the number y+z and put this value in the variable x".

Pitfall. In C++, the symbol ^ does not mean "to the power of". It is an operator, but not a useful one for beginners. Do not use it in this module.

Hopefully some of the important concepts like loop-inside-a-loop and variables will make more sense if you think of register machines and how these concepts work there.

Finally, in C++, just like for register machines, you can have as many variables as you want. Many problems are made easy by introducing new variables. You will not always be told what variables you need, so feel free to add new ones as you need them.

4. Notes

Note 1. Register Machines were defined in C++ using macros. Macros are occasionally essential, but only for very specialist applications, and never for beginner-type programs (except for very special didactic purposes like here). Do not use them.

Note 2. We also saw the line "using namespace std;". This is a convenience for beginners and is OK for small programs, such as all the ones here. It is not OK for large programs and experts will complain if you use it. (Without "using namespace std;" you are supposed to precede various names with std:: such as "std::cout".) For this course "using namespace std;" can always be used, but if your programming goes beyond this course, you should avoid it.