Learn C++ with Qt, Part 007: Constants


After having covered the basic C++ data types in part 006 of this tutorial, let’s have a look at the definition of constants.

What is a constant?

A constant in a program is the same as a mathematical constant: it is an expression of a certain type with a value that does not change during a computation or any run of the program. Mathematics knows generic constants like pi (3.1415…), and in physics there are constants for the speed of light and a lot of other things.

In mathematical equations, there is also the concept of a temporary constant, which is a variable that has a fixed value only for a specific computation, with a possible different value for the next computation.

For a computer program, a constant is just a variable that has a fixed value for the whole runtime of the program.In contrast to a normal variable though, any try to assign a new value to it after the initialization leads to an error.

In addition to that, you always need to set a value for a constant when you define it, whereas variables can be assigned initial values separately after the declaration.

Different types of constants

In C++, it is possible to define a constant for most of the basic, build-in data types: char, int, float, long etc.

Just as with variables, depending on the value and how and where you want to use it in your program, you need to be clear about the data type of any constant you define.

Literals

In order to define – or directly use – a constant value we need to use a so called “literal” in the related line of code in our program.

A literal is just a specific, concrete value of a given data type. In a direct value assignment such as

int x = 10;

where the integer variable “x” is assigned the value “10”, “10” is the literal.

Numeric literals (numerals)

For integer values, the explicit number can be used as a literal, no matter if the value is positive or negative. With a negative integer value, you can just add the minus sign directly in front of the value.

In C++, integer values can also be expressed by using octal (base 8) or hexadecimal (base 16) numbers instead of decimal (base 10) numbers. This can sometimes be useful as these numbers directly relate to the byte-values (1 or 2 bytes) of the variable inside the memory, especially in more technical, hardware-related programs. Octal numbers start with a “0” (zero), followed by one or more digits with values between zero and seven. Hexadecimal numbers start with “0x” (zero followed by “x”) and use digits with values between zero and nine, as well as the characters “a” to “f” (for values between ten and fifteen).

As you know from part 006 of this tutorial, integer variables can be defined with different type modifiers which can suppress the sign before the value (unsigned) or reserve more memory in order to store bigger values (long, long long). If you want to define a constant with the same integer modifiers, you have to add a suffix after the numeric value: “u” or “U” for “unsigned”, “l” or “L” for “long”, “ll” or “LL” for “long long”, or a combination of that.

For floating point numerals, you also need do add a suffix, dependent on the type you want to use. With a value of the type “float”, the suffix is “f” or “F”, and for a “long double” you need to add “l” or “L” to the value.

Apart from standard floating point numbers which consist of some value followed by a point and additional decimals, the numeral for a floating point value can also use the “e”-notation, where “e” stands for the mathematical expression “times ten to the power of”, which is a kind of a shorthand notation for especially big or small values.

Here is an overview of the different kinds of numeric literals with their data type and some examples:

Data type Suffix Example Remarks
int 42
-7
052
0x2a
no suffix necessary
negative value
octagonal value
hexadecimal value
unsigned int u
U
125u
125U
only positive values
long l
L
130l
130L
big values
unsigned long ul
UL
lu
LU
65ul
65UL
65lu
65LU
big, positive values
long long ll
LL
240ll
240LL
huge values
unsigned long long ull
ULL
378ull
378ULL
huge, positive values
float f
F
3.14159f
3.14159F
floating point numbers
double 6.952
2.356e4
default type for floating point numbers without additional suffix
long double l
L
5.396485e-5L Note: if the L suffix is used in combination with a floating point number, the compiler assumes that the type is “long double”; if it is used with an integer number, the type is set to “long int”.

Character and string literals

Literals for single characters or strings consist of a single character or a string of characters that is enclosed by quote signs.

Single characters

For a single character, we need to use the single quotes, for example:

'A' 'n' '\b' '4'

As the examples above show, you can use any kind of character for a single character literal. This includes number characters and escape sequence characters (which I’ve already listed in part 003 of this tutorial).

Note that any number inside quotes is interpreted as a character, not as a number. If you want to use it for any computation, you need to convert it into a numerical type first.

Another thing to keep in mind is the similarity between single character literals and variables which use only a single character as their name:

I 'I'

In the above example, the first “I” is interpreted as the identifier for a variable, while the second “I” is interpreted as a single character literal because of the single quotes.

Strings of characters

For string literals, the string of characters has to be put in double quote signs, like this:

"This is a string." "The quick brown fox jumped over the lazy dog."

As a string can contain any kind of character, you can include special characters, numbers and also escape sequences in your string. Here are some examples:

"First line\n Second line \n Third line." "The sheer # of 123… etc. is impressive, isn’t it?"

The second example shows that inside the string literal, any single quote sign is just part of the string. So if you use something like

"And again, 'X' marks the spot!"

the character “X” and the surrounding single quotes are contained in the string and will be part of the output if you print this.

If you use more than one string literal in the same line of code – where the single line of code can span more than one line – the literals are automatically interpreted as one single, long literal. For example:

"The quick" " brown fox " "jumps over" " the lazy dog"

The example above will automatically be combined into a single literal when the program is executed: “The quick brown fox jumps over the lazy dog.” Any spaces, tabs and line breaks outside of the string literals (not between double quotes) are ignored, anything between the double quotes is combined into one string literal.

This concatenation of string literals only happens as long as there is no marked line end, which in C and C++ is a semicolon.

For long string literals which do not fit into a single line of code, you can either concatenate several shorter string literals manually or use the backslash character in order to tell the compiler that the string literal is continued in the next line, like this:

myString = "This is a long string divided into \ two lines of code."

This is equivalent to:

myString = "This is a long string divided into two lines of code."

Prefixes for character literals

Similar to the different suffixes for numeric literals which let you set the literal value to a specific data type, you can use different prefixes for single character literals and strings.

In the previous part of this tutorial I already listed the different data types for character variables. There are specific prefixes for character literals which refer to these different types, as you can see in the table below:

Data type Prefix Example Remarks
char 'A'
'\n'
no suffix necessary
char16_t u u'B' 16-bit character
char32_t U U'C' 32-bit character

Prefixes for string literals

While the string data type is always just a succession of characters that is usually ended with a “null” character, C++ allows two additional variations when it comes to string literals: raw strings and UTF-8 encoded strings.

In a raw string, you can include the backslash, as well as single and double quotes as part of the string in order to include them in the output. A raw string literal can be defined by using the prefix “R”, followed by a double quote and a left bracket, the string data and a right bracket and another double quote. Everything that appears between the two brackets “(“ and “)” will be included in the string output, while any sequence of characters outside the brackets is ignored.

A string with UTF-8 encoding is a Unicode string that uses the UTF-8 encoding for the characters contained within. In contrast to the default ASCII characters, which are identical to the first 128 UTF-8 characters, the Unicode characters cover a wider range of special characters from different languages. Apart from the first 128 characters, they internally use 2 to 4 8-bit bytes to store the character values. You can get more details about UTF-8 on the related Wikipedia page.

Here is a short overview for the different kinds of string literals:

Data type Prefix Example Remarks
string "Hello again." no suffix necessary
string (raw) R R"(This is a \backslash and some "'quote'".)"
R"&%$(another string with \backslash)&%$"
Anything between "( and )" is included in the string, any sequence of characters outside the brackets is ignored.
string (Unicode) u8 u8"This is a Unicode \x0065 character." With the "u8" prefix, the string is automatically encoded into a string of Unicode characters using the UTF-8 encoding.

Literals for Boolean and null pointer data types

You can also use literals for the Boolean and null pointer data types. In contrast to numeric or character types, you can only use specific keywords as literals here.

For Boolean values, the allowed keywords are "true" and "false", which directly relate to the Boolean value they represent.

With null pointers, the only allowed keyword literal is "nullptr".

Here is a short example:

bool myBool = true; long* myPointer = nullptr;

Defining constants with specific data types

Outside of literals which can be used to assign a constant value to a variable, constants can also be defined using explicit data types in the same way that variables are defined. This allows you to use the same value in different parts of your program without having to write down the explicit value again and again. Instead, you can just use the name of the constant that you defined, which also makes the code more readable.

You can turn the definition of any variable that uses a standard data type into the definition of a constant by adding the prefix keyword “const” to the definition and assigning an initial value as part of the definition. For example:

const double pi = 3.1415926; const char newline = '\n'; const string errorMessage = "This is an error – don't panic…";

Constants that are defined like this can be used in the same way that variables are used. As with variables, you occasionally might have to do some type conversion for specific computations.

Here is a short example program that uses “typed” constants:

#include <QtCore/QCoreApplication> #include <iostream> using namespace std; const double pi = 3.14159; const char newline = '\n'; int main (int argc, char *argv[]) { QCoreApplication a(argc, argv); const string inputmessage = “Please enter the circle radius:”; const string outputmessage = “The circle’s area is: “; double radius = 0.0; double circle; cout << inputmessage << newline; cin >> radius; circle = 2 * pi * radius; cout << outputmessage << circle; cout << newline; return a.exec(); }

In this short example program you can enter a floating point value for the radius of a circle. The program then takes the input and calculates the circle area using your input and the defined constant for pi. After that, you get an output message followed by the calculated area value.

While typed constants are defined similar to variables, their value cannot be changed when the program is executed. You cannot assign different literal values or calculated values to a constant after the constant has been initialized. Doing so will produce an error.

Defining constants with preprocessor definitions

One last option to define constants in a way that allows you to use them over and over is using preprocessor definitions to define them.

I’ll tell you more about preprocessor definitions in a later part of this tutorial, so I’ll stick to the most important details right now.

Preprocessor definitions are used to replace custom identifiers in your code with a pre-defined replacement. The identifier is a custom, single word name that is written in capital letters, and the replacement can be any literal or string of characters.

A preprocessor definition is defined at the top of the program, after the references to the used libraries and namespaces, before the rest of the code, using the keyword “#define”, like this:

#define IDENTIFIER replacement

When your program is compiled, the preprocessor checks the code of your program and replaces any identifiers it finds with the replacement you defined, before passing the code to the compiler. This change only happens inside the memory, so your source code will not be changed.

Here is a version of the previous program to calculate a circle area that uses preprocessor definitions instead of the typed constants:

#include <QtCore/QCoreApplication> #include <iostream> using namespace std; #define PI 3.14159 #define NEWLINE '\n' #define INP_MESSAGE “Please enter the circle radius:” #define OUT_MESSAGE “The circle’s area is: “ int main (int argc, char *argv[]) { QCoreApplication a(argc, argv); double radius = 0.0; double circle; cout << IN_MESSAGE << NEWLINE; cin >> radius; circle = 2 * PI * radius; cout << OUT_MESSAGE << circle; cout << NEWLINE; return a.exec(); }

There are two things to look out for if you use preprocessor definitions to define constants:

  1. The preprocessor definition does not need the semicolon to mark the line end, the definition for the replacement automatically ends with the line break. If you add a semicolon, then the semicolon will be part of the replacement for the identifier.
  2. For constants that do not use a default data type (“int” for integer numbers, “double” for float numbers, “char” for single characters and “string” for strings), you have to use the related suffix or prefix (see the tables above).

Final words / Code download

Now you have an overview of how to define and use constants in your programs. Together with variables – which we covered earlier – this is useful for calculations where you often need the same values as part of the equation several times.

The code for this part of the tutorial can be found in the "Variables-Constants" subdirectory within the complete tutorial code repository on GitHub. If you have git or GitHub Desktop installed on your computer, you can clone the git repository, if not, you can download it as a ZIP archive.

I hope you are coming back for the next part in the C++ with Qt tutorial!