본문 바로가기

Programming/C++ Basic

[Basic C++] 23 - Elaborated explanation for "const" keyword in C++

In the previous post (2020/07/27 - [Programming/C++ Basic] - [Basic C++] 22 - Declaration and Definition), the fundamental concepts of declaration and definition are explained roughly. They are the two most crucial concepts that programmers must understand deeply to generate proper code in C++.

 

In this post, I am going to talk more about declaration and declaration. The way to declare variables in C++ is quite simple; however a number of programmers, no matter how good they are, sometimes suffer from understanding certain forms of complex statements.

 

A lot of students and engineers including me start to learn C++ to create purpose-driven programs to solve complex mathematical problems. The thing is, C++ is not just a programming language to generate programs, but a good window for understanding how modern computers work behind the scene.


1. Configuration of declaration in C++

In C++, declaration consists of largely four parts within one statement. There are non-type specifiers, type specifiers, declarators, and initializers (See Figure 1). They are also referred to as "declaration specifiers".

 

Figure 1 Declaration specifiers

Non-type specifiers are specifiers directly related to declarators such as const, volatile, and static. They exist independently of type specifiers, but only one non-type specifier is allowed to be used for one variable. On the other hand, type specifiers are specifiers determining the data type of given variables. They can be the built-in data types such as int, float, and double, or user-defined data types such as class, union, and struct.

 

Declarators are the names of variables (or function) being defined. Multiple declarators can be declared separated by comma (,). Thus the next statement declares two int variables "a" and "b".

int a, b;

Initializers are values assigned to declared variables. They are called "rvalue" but it will be explained later in detail with "lvalue". You are always advised to initialize variables because some garbage values that exist in the current memory address are assigned to the declared variables.

 

Figure 2 Declaration and definition of various variables in MSVC 2019
Figure 3 The order of specifiers

Back to specifiers, they can be declared independent of the order that they appear in a statement. Figure 2 and Figure 3 show that a number of variables declared with different specifiers. Particularly Figure3 shows the order of specifiers is not a problem for the compiler to understand it. Programmers just throw a pile of specifiers and the compiler structures them automatically.

 


2. Declaration specifiers : Non-type vs type specifiers

 

There is a significant difference between non-type specifiers and type specifiers. Non-type specifiers apply to a variable directly, and type specifiers apply to each other for determining the data type of the variable. Figure 2 shows how specifiers work on each other by the compiler.

 

Figure 2 How specifiers work on each other

 

3. Meaning of "const"

The "const" and "volatile" keywords are type specifiers, not non-type specifiers. They apply to other specifiers, not directly to the variable unless they are located after the dereference operator (*). In this post, The "const" keyword is mainly explained to understand how it works in detail. Look at the example code below.

 

Figure 3 shows various declarations of integers and cont integer variables and pointers with compilation errors. There are five types of variables declared in the example.

 

[1] integer variable

: An integer variable with an initializer.

 

[2] const integer variable

: An integer variable with an initializer but const.

 

[3] integer pointer

: An integer pointer that points to an integer variable

: An integer pointer that points to a const integer variable -> ERROR

 

[4] const integer pointer

: A const integer pointer that points to an integer variable

: A const integer pointer that points to a const integer variable

 

[5] integer const pointer

: An integer const pointer that points to an integer variable

: An integer const pointer that points to a const integer variable -> ERROR

 

Figure 3 Const variables declarations and erros

Within the main function, 7 assignment operations are conducted and several of them occur errors. This is a quite complicated example if you do not understand the "const" keyword and specifiers in C++.

 

The next statement is complied perfectly, as "a" ins a modifiable integer.

a=50;

But the next statement is not compiled, as "b" is const. It is not modifiable.

b = 50;

And the next statement is compiled, as the pointer "p1" is not const. It can point to any integer variables.

p1 = &c;

But the next statement is not compiled, as the pointer "p1" is not const. To point to a const variable, the pointer itself must be const too. In other words, by assigning a const variable to a non-const variable, the rvalue loses "const" (which is not allowed).

p1 = &b;

The next statement is compiled, as the pointer "p4" is const and the variable "a" is not const. In this case, a const pointer can point to a non-const variable, as the operation adds "const" to the variable.

p4 = &a;

However, the next statement is compiled, as the pointer "p4" is just a const pointer. You can change where it points to, but you can not change the value of what it points to.

p4 = &c;

Then, let's see the next statement. It complies well because the pointer "p5" is not a const pointer, but a pointer with const address. You can not change where it points to, but you can change where it points to.

*p5 = c;

Figure 5 explains how const pointers work depending on the location of "const" keyword. If "const" is before the dereference operator, the value being pointed is considered const by the pointer, if not, the pointer value itself is const.

 

Figure 5 Const pointers

Using const pointers is a very good practice to keep variables from being altered accidentally, even if the variables are not const. In other words, when you use some variables, you promise the compiler that you are not gonna alter it during the process.