Fundamental to programming is the concept of a variable, a piece of data that can change as the program runs. A variable could be an integer such as a page number in a book, a character such as the first letter on the page, a floating point (decimal) number such as the height of that character in centimetres, or many other things, but the important similarity between them is that they can all change.

C++ is a statically-typed language, meaning that a variable can only store a certain kind of data—such as an integer, character, or floating point number—and that type must be declared when the variable is introduced. Whilst the contents of a variable can change, it’s type cannot. The common built in types in C++ (those available without including any parts of the standard library) are

  • int - Stores integers such as 4 or -3900. Is at least 16 bits long, but usually 32.
  • char - Stores a single character such as 'c'. Is at least 8 bits long.
  • float - Stores floating point values such as 3.1555. Is usually 32 bits long.
  • double - Similar to float but with twice the precision. Is usually 64 bits long.
  • bool - Stores true/false values.
  • void - Cannot store data directly, but acts a bit like a placeholder.

Usage

Before a variable can be used to store any data it must be declared by first specifying the type, then the variable name (which must start with a letter but can then contain any alphanumeric character or underscores, and can be of any length), and then ending the line with a ;. A variable declaration is an example of a statement, and all statements in C++ must end with a ;.

// Create two integers called foo and bar
int foo;
int bar;
// Create a float called baz
float baz;

We can then assign values to the variables and perform the usual arithmetic operations, though the relevance and availability depends on the type (you can’t do much to a bool).

foo = 6;
bar = 10;
foo = foo + bar * foo;

baz = 7.4f; // The f means that this is a float and not a double
baz = baz * baz;

At the end of that foo will have the value 66, bar will have the value 10, and baz will equal 54.76. Note that unlike in mathematics where = denotes equality, in C++ = is the assignment operator and will change the value of the variable on the left hand side to the result of the expression on the right hand side.

It’s cumbersome to split an assignment and a declaration into two lines, so C++ allows you to combine the two into an initialisation, as well as combine multiple initialisations together.

// foo is an int with value 75, bar is an int with value 5, and baz is
// an int which hasn't been assigned a value.
int foo = 64 + 9, bar = 5, baz;

// Note that only c contains the value 10.0f, a and b don't have a value!
float a, b, c = 10.0f;

If a variable is declared but not assigned a value it will often be zero, but don’t count on it, always initialise! (Don’t do what I did in the first example.)

The operations valid on all numerical types (including char, which stores characters as numbers as described ASCII/UTF-8) are +, -, *, and / as you would expect. One caveat with / however is that because int can only hold integers, / will truncate decimals and only leave the integer part. Unlike some programming languages, there is no exponentiation operator (^ exists but it’s unrelated). Specific to integer types is the modulo operator %, which returns the remainder in a division.

int foo = 7 % 3; // Contains 1

There are also additional unary operators ++ and -- (the increment and decrement operators) which only take one operand instead of two. The first increases the value of a variable by 1, and the second decreases the value by 1. They can be placed both before and after a variable, but their behaviour is subtly different in each case.

int foo = 0;        // Foo contains 0
int bar = foo++;    // Bar contains 0, then foo is incremented to 1
int baz = ++foo;    // Foo is incremented to 2, then baz is set to 2

It’s good style to stick to either post-incrementing (foo++) or pre-incrementing (++foo) when the order doesn’t matter and only use the other when the order is relevant. I prefer to pre-increment, because the computer can do it faster than post-incrementing.

Additionally the assignment operator can be combined with any binary (acts on two variables) operator discussed above as a shorthand.

int foo = 10;
foo += 5; // Equivalent to foo = foo + 5
foo *= 10; // Equivalent to foo = foo * 10

This also applies to several other operators that we haven’t discussed, not just the arithmetic ones.

Modifiers

C++ provides a selection of modifiers that can be applied to variable types when they are declared, creating a much larger list of variable types. The main modifiers are signed, unsigned, short, long, and long long. signed and unsigned specify—as they imply—whether or not the sign of an int or char should be taken into account. Since it takes 1 bit to store the sign, by gaining the ability to use negative numbers the maximum value of the variable is halved.

// -128 <= a <= 127
signed char a = -44;
// 0 <= a <= 255
unsigned char a = 56;

When declaring an int the signed is implied, but when declaring a char you should be explicit as it’s left up to the compiler whether it is signed or unsigned. If you’re dealing with actual characters then you should use char without any modifiers, but if you just want to store small numbers then specify either signed char or unsigned char to be safe.

short, long, and long long alter then number of bits used for the variable and hence affect the range of numbers that can be stored. These can only be applied to int, and can be combined with signed and unsigned.

// At least 16 bits, usually exactly 16
short int a;
// The int can be omitted when any of the length modifiers are used,
// and also when using signed and unsigned
short b;
// At least 32 bits, usually 32 on a 32-bit computer and 64 on a 64-bit
unsigned long int c;
long d;
// At least 64 bits, usually exactly 64
long long int e;

Finally, long can be applied to the double type to increase the precision even further, but it cannot be applied to float (wouldn’t a long float just be a double?) and the standard double type is sufficient in most situations.

There is also an additional modifier, const, which declares a variable to be constant. This goes against the concept of a variable somewhat, because by declaring a variable as const you tell the compiler that its value will never be changed and it will instead keep the value it was initialised with. If you try to change a const variable, the compiler will error.

// Declare an integer constant with the value 3
const int my_constant = 3;
// This isn't allowed
my_constant += 3;
my_constant = 10;
// But this is
std::cout << 2 + my_constant;
// You can also combine const with other modifiers
const unsigned long long foo = 132423121223;

The last modifier we will cover is auto, which instructs the compiler to deduce the type of a variable from the value it is being initialised with; you replace int, double etc. with auto and let the compiler figure things out. It’s good practice to explicitly declare types if you can but it’s a very handy feature when dealing with long type names which we’ll come across later.

auto foo = 10; // int type
auto bar = 10.3f; // float type
auto baz = 'c'; // char type

Type Conversion

Ordinarily two types are incompatible with each other, so you can’t assign a char value to an int variable, however C++ automatically converts between types to make this possible. Type promotion occurs when a value with a small type is assigned to a variable with a larger type, e.g. signed char and signed short are promoted to int, and floats are promoted to doubles. This means the following is valid even though the types don’t agree.

char c = 'c'; // Normal assignment
int i = c; // c is promoted to an int
double d = 3.5f; // 3.5f is promoted to a double

When promotion cannot occur, a type conversion might. These are pretty logical and work how you would expect; floats are rounded down when assigned to ints, for example.

double d = 10.3954232324;
float f = d; // The extra precision of d will be cut off
int i = 10;
char c = i; // i is small enough to be contained in c

You can also explicitly convert types by using type casting, which has two equivalent forms.

double d = 3.14;
// C style type casting
unsigned i = (unsigned)d;
// C++ style functional cast
unsigned j = unsigned(d);
// The syntax doesn't allow for functional casts such as
// unsigned int(d), use a C style cast instead

Arrays

If we want to store multiple related values, it’s annoying having to declare each one individually. We can avoid this problem by defining an array, which is a sequential list of variables of the same type. An array can be made from any type (not just the built in ones we’ve seen so far). Each individual variable can be accessed by its index, or position in the array.

// Make an array of 3 ints
int foo[3];
// Set the values in the array individually
foo[0] = 10;
foo[1] = 15;
foo[2] = 30;
// Make an array of two long doubles and fill it with two values
long double bar[2] = { 3.141592653589, 0.79323846264 };

[] in C++ are reserved for array element access and creation; in declarations they specify the number of elements in the array, and in expressions they access the given element. Note that array indices in C++ are zero-based—the first element is at position 0, not 1 (unlike some other languages). This may seem counter intuitive at first, but it’s vastly more useful! (Though I admit it has led me to sometimes start counting from zero, with embarassing results.) We won’t see the use until later when we cover loops and pointers though, so you’ll just have to trust me.

Instead of assigning values to each element of the array individually, we can initialise the array with a set of values using an initialiser list specified by {}. There does not need to be the same number of elements in the array as the list (though there must be a greater or equal number), although elements not given a value will be uninitialised. Additionally when initialising an array using {}, you can omit the size of the array and let the compiler work it out for you.

// This will create an array of 5 chars
char word[] = { 'h', 'e', 'l', 'l', 'o' };

If you are only interested in initialising specific elements in an array, you can pass individual element indices to the initialisation list. The behaviour is best illustrated with an example.

// foo and bar are equivalent
int foo[] = { 1, 2, 3, [8] = 9, 10, 11 };
int bar[11] = { 1, 2, 3, 0, 0, 0, 0, 0, 9, 10, 11 };

References

Consider a situation where we have an array of values and want to use one of them in a later piece of code. What’s more, we want to be able to modify that value and see the changes reflected in the array. We can’t just set a variable to the required value, because changing that variable won’t affect the value in the array; they’re completely separate things that just happen to have the same value. One solution is to carry around the index of the element, but then we have to keep on accessing the array.

int array[5] = { 1, 2, 3, 4, 5 };
// Want to change the 4th element
int index = 3;

// Array is modified
array[index] = 10;
array[index] *= 2;

C++ offers an alternate solution by way of references. A reference provides an alternate label for a variable, such as an element in an array. References are treated a lot like actual variables, but anything we do to the reference happens to the original variable too.

int array[5] = { 1, 2, 3, 4, 5 };

// Element is a reference to an int, specifically
// the variable at index 3 in the array
int& element = array[3];

// Modify the variable the reference refers to
element = 10;
element *= 2;

// Both numbers will be the same
std::cout << "element " << element << " array " << array[3];

References are purely labels for existing things, they aren’t guaranteed to even exist in the compiled code because the compiler can optimise them out and replace them with the variable they refer to. This means that you can’t have arrays of references, or references to references.

int variable = 10;

// refA is a reference to variable
int& refA = variable;

// refB is not a reference to refA, but a reference to variable
int& refB = refA;

Basic Input and Output

Before we proceed it makes sense to briefly describe how to input and output data. This is quite an in depth topic but since it’s so crucial we’ll give it a short bit of attention.

Input and ouput in C++ works through the use of streams, which are essentially sources and destinations for data. Input streams accept data from a file or the user, and output streams send data to other files or back to the user. If you want to use input and output then don’t forget to include the iostream header as discussed previously.

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main()
{
    int userInput = 0;

    std::cin >> userInput;

    std::cout << "That's half of " << 2 * userInput << std::endl;

    return 0;
}

cin is the standard input stream between the keyboard and the program, and cout is the standard output stream between the program and the command prompt/terminal window. The std:: in front denotes them to be part of the C++ standard library, and is required in front of anything contained in included standard library headers such as iostream or cstdlib.

The >> and << are stream operators, which point in the direction the data flows in—from the input to the output—and must be used with their respective stream.

Note that std::cin will pause the program and wait until the user presses Enter before continuing. It will also only read up to the first piece of whitespace, so if the user enters 33 54 the program will only see the 33. This pausing can be a handy property as it allows you to keep the program open if it closes immediately (this happens if you just run the program instead of opening it from a command prompt/terminal), although a more streamlined way is to use

std::cin.get();

which will also pause the program but does not need any variable unlike std::cin >> .

The >> and << operators can also be chained together in order to process multiple input or output statements in a row, and is very useful to split up different types which cannot be combined in the same statement. The example above first outputs a string of characters, then outputs an int, and finally prints a single character given by std::endl, which tells C++ to output the newline character '\n' (same as pressing Enter) and display the text on the screen.

Exercise

Hopefully now you’ve got a decent grasp of variables and how to manipulate them, but pratice is important so as a brief exercise you should write a program that retrieves three floating point values from the standard input and then prints their average.

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

int main()
{
    // You could also use an array here
    float a = 0.0f, b = 0.0f, c = 0.0f;
    float average = 0.0f;

    std::cout << "Enter three numbers separated by spaces\n";

    std::cin >> a >> b >> c;

    average = (a + b + c) / 3.0f;

    std::cout << "Their average is " << average << std::endl;

    return 0;
}