Variables and typing

In processing variables, C++ has more details than Python. However, you need not worry, although getting started feels slow to you.

Python vs. C++: Variables and input

Let’s compare the following two programs (which work exactly the same):

def main():
    name = input("Input your name: ")
    age = int(input("Input your age: "))

    print("Nice to meet you", name, age)
    print("After", 50 - age, "years you'll be 50.")

main()
#include <iostream>
#include <string>

using namespace std;

int main() {
    string name = "";
    cout << "Input your name: ";
    getline(cin, name);

    int age = 0;
    cout << "Input your age: ";
    cin >> age;

    cout << "Nice to meet you " << name << " "
         << age << endl;
    cout << "After " << 50 - age
         << " years you'll be 50."
         << endl;
}

Remarks and conclusions:

  • C++ program is often longer that similarly functioning Python program. One example is not a proof (obviously), but experience in general seems to support this idea.

  • In Python one can start using a variable just by assigning it a value with an assignment command (=). Python is not very strict about the type of information you store in a variable and during the program execution a variable can contain values that are of different type.

    In C++ a variable must always be declared before it can be used. A declaration tells the C++ compiler the name of the variable we want to use in the future and as importantly what type of information can we store in it.

  • In Python variable and function names can contain any characters that are considered letters. For example scandinavian letters are accepted. In C++ variable and function names must consist of ASCII-letters, digits, and underscore (_). The first character must be a letter.

  • In strings, scandinavian letters are accepted also in C++. However, in some situations they can be problematic, so it is good to avoid them.

  • Unlike in Python, the data type that we use to manipulate text (string) is not part of the language: it is implemented as a library. Therefore if we need to use strings in C++ programs, we must include the string library, i.e. write #include <string> in the beginning of the program.

  • Unlike print function in Python, cout in C++ doesn’t automatically add spaces between the values it prints. It doesn’t add newline at the end either, unless endl is manually added.

  • Variables cin and cout are C++’s mechanism to handle keyboard and screen in C++ code (input and output operations).

  • The primitive ways to read text from input stream cin are function getline for reading strings and input operator >> for reading a value of a suitable type for a variable.

  • Note that input function in Python and getline function in C++ work in different ways. The former one takes the printable string as a parameter and it returns the user-given string. The latter takes two parameters, the first of which is an input stream (that is always cin in the beginning of the course). The second parameter is a variable, which the input value will be assigned to. Note that the return value of getline cannot be assigned to a string.

  • Input operator >> skips all empty characters, also line breaks. This means that, the input is read until the user gives visible characters. Test this by inputting a couple of line feeds before giving a number as the age. This may feel strange when compared to Python, where we have used to read input line by line with input function.

  • This is enough to let us start. We will later study IO stream in more detail, and we will also learn how to check the correctness of user input (when the user input with >> is of an invalid type).

Variables in C++

In C++ a variable must be defined (or at least declared) before it can be used. The definition of a varible contains two parts: declaration and initialization. Declaration consists of two parts, and thus, there are totally three parts:

  1. the type of the variable
  2. the name of the variable
  3. initialization of the variable (to set an initial value).

Parts 1 and 2 (declaration) are mandatory. Some concrete examples of variable definitions:

// Variable definitions without initialization (i.e. declaration)
int age;
double temperature;
string name;

// Variable definitions with initialization
int age = 21;
double temperature = 16.7;
string name = "John";

A declared but uninitialized variable has an undetermined value until it is set somehow (e.g. with cin >> or = operator):

int age;
double temperature;
// At this point both age and temperature have undetermined values

age = 21;
cin >> temperature;
// Now both variables have determined values

Caution

Uninitialized variables may produce programming errors that are very difficult to trace. You follow a good programming style, when you always initialize variables in definition.

C++ is strict about the type of a variable: one can only store into a variable a value of the same type as the variable’s type is.

Scope of a variable

Local variables are defined inside a block (a pair of curly brackets {}) and it can be used from the point of its definition all the way to the closing bracket of the block it was defined in:

int main() {
    int guests = 0;
    while ( guests < 100 ) {
        int free_seats = 100 - guests;
        ... lines removed ...
        // This is the last point where variable free_seats can be used.
    }
    ... lines removed ...
    // This is the last point where variable guests can be used.
}

Data types

The data types in C++ that most of the simple programs can be implemented with are  int,  double,  bool,  and  string. C++’s double is basically the same what was called float in Python (decimal number).

In addition to these types that were pretty much familiar from Python, C++ has a data type called char which is used to manipulate single characters. A simple example of using char type is:

char gender = 'F'; // F for female

Literals and constants

Term literal means a nameless constant value, for example: 42,   1.4142,   'x',   "text", or  true.

Notice the following differences between Python and C++:

  • Python has no separate data type for manipulating single characters, instead they are handled as strings which contain only one character.
  • In Python it makes no difference whether you use single quotes or double quotes to express string literal. In C++ string type literals are written inside double quotes as "abc", and char literals inside single qoutes as 'x'.
  • Therefore, for example, ´X´ is a character, and "X" is a string of a single character. String "X" cannot be assigned to a variable, the type of which is char.

There is one extra feature concerning C++ variables that doesn’t have a counterpart in Python: constants. They are basically variables whose value can’t be changed after it is set in the initialization.

const double PI = 3.14;

... lines removed ...

// Changing the value of constant yields an error message
PI = 3.141592653589793; // ERROR

Constants are meant for naming such values that always remain the same. Natural constants as pi are straightforward examples, but there are other uses, too:

const int LOTTO_BALLS = 7;

Because you cannot change the value of a constant after definition, it is quite logical that it must be initialized with declaration.

It is a common practise to name constants with ALL_CAPITAL_LETTERS, and if the name of the constant contains multiple words, the spaces are replaced with underscore. Descriptive constant names make the source code easier to understand.

Dynamic vs. static typing

How does the following Python program behave?

def main():
    print("Start")
    var_1 = 5.3
    var_2 = 7.1
    print(var_1 + var_2)
    var_2 = "Hello"  # Recycling var_2 for a new purpose
    print("Midpoint")
    print(var_1 + var_2)
    print("End")

main()

When the program is executed it will fail on line 8 with the following printout:

Start
12.399999999999999
Midpoint
Traceback (most recent call last):
  File ``code/part-01-04.py'', line 11, in <module>
    main()
  File ``code/part-01-04.py'', line 8, in main
    print(var_1 + var_2)
TypeError: unsupported operand type(s) for +:
  'float' and 'str'

This behaviour is a result of dynamic typing which is used in Python. It means that the validity of the data is only checked when we try to use the data.

In practise dynamic typing in a programming language results:

  • One variable can contain different type values during the execution of the program.
  • The program will run fine until we reach the point where we try to do something with values that are not compatible.

Dynamic typing is very common in interpreted programming languages.

The same example in C++ would look something like this:

#include <iostream>
#include <string>

using namespace std;

int main() {
    cout << "Start" << endl;
    double var_1 = 5.3;
    double var_2 = 7.1;
    cout << var_1 + var_2 << endl;
    string var_3 = "Hello";  // New variable
    cout << "Midpoint" << endl;
    cout << var_1 + var_3 << endl;
    cout << "End" << endl;
}

If we try to compile this program we will get a compilation error (simplified here to make a point):

main.cpp:13: error: no match for 'operator+'
             cout << var_1 + var_3 << endl;

C++ uses static typing: if we try to manipulate data in our program in a way that C++ language doesn’t allow/understand, we will get a compilation error.

In practise when we use statically typed language the following are true:

  • Variables must be defined before they can be used.
  • Variables have a permanent type and only the data that is of the same type can be stored in the variable.
  • If there is an error in the program related to types, the compiler produces an error message and the program will not be compiled. Therefore it can’t be executed, tested, or enjoyed in any other way.

Static typing is very common in compiled programming languages.

Notice how on line 11 in our C++ test program we had to define a new string variable var_3, since we cannot assign a string to var_2, although this is possible in Python.

Good side of the dynamically typed languages is that programmer is saved from micromanaging the types of variables i.e. not having to define every single variable manually. Many programmers also like the possibility of using some variables to store different type values.

Negative sides of dynamically typed languages include the very real problem of producing a program that seems to work but actually has some hidden errors lurking somewhere. This happened in the example program, when we tried to add a real number with a string. In addition, using the same variable for different purposes is not a good idea from the point of view of code understandability.

Type conversion

In type conversion, the compiler considers the data of a variable as a different type, from that given in the definition of the variable. Type conversion is either implicit or explicit.

Compiler manages implicit type conversion automatically. For example, it can convert single precision float to double precision double automatically:

float f = 0.123;
double d = f;

Or it can convert int to double:

double d = 3;

Moreover, it can convert integer to a character:

char ch = 113;  // ch is 'q', the ASCII value of which is 113.

Explicit type conversion happens, when the programmer wants to define how to consider the data of a variable as of a different type. In such case, the programmer uses operator static_cast.

For example, comparison between a signed and unsigned int is suspicious, and thus, the compiler gives a warning:

int i = 0;
unsigned int ui = 0;
...
if( ui == i ) {  // This line yields a warning
   ...
}

If the programmer is absolutely sure that he/she wants the compiler to see a signed int as an unsigned int, he/she can do the type conversion explicitly, and the warning vanishes:

if( ui == static_cast< unsigned int >( i ) ) {
   ...
}

The following example emphasizes for the reader that a character is considered as an integer:

cout << "Input a character: ";
char ch = ' ';
cin >> ch;
int ascii_value_of_ch = static_cast< int >( ch );
cout << "ASCII value of " << ch << " is " << ascii_value_of_ch << endl;