Some thoughts on the role of the const keyword in C/C++

1. The introduction of the const keyword

const is the abbreviation of the English word constant, constant, constant (in actual use, to be precise, it should be "read-only"). In C language, const is used to modify variables, so programmers want this variable to become "constant", but quotes are added here, because for C language, we can still modify it indirectly through pointers.

2. const modifies ordinary variables and pointer variables

The const modified variables in the C language have the following specificities:

  • The variable modified by const is only read-only, and it is still a variable in essence, not a constant;
  • Local variables modified by const allocate memory space on the stack;
  • The global variables modified by const allocate memory space in the read-only storage area;
  • const only works at compile time, not at run time.

In general, in the C language, the variable modified by const is not a real constant, it just tells the compiler that this variable cannot appear on the left side of the assignment symbol (used as an lvalue).

2.1 const modifies ordinary variables

const int a = 10;
int const a = 10;

a = 20;

The above two writing methods are the same, const modifies variable a, which means that variable a cannot be changed by assignment, and can only be initialized to an initial value by definition.

2.2 const modified pointer variable

Modifying pointer variables is the essence of const gameplay. There are several situations:

/* 这两种写法是一样的,const修饰的是p所指向的内存空间,就是指针p所指向的内存空间的值(或者是内容)是不允许被修改的 */
const int *p;
int const *p;

/* 这里const修饰的是指针p本身,就是指针所指向的地址是不允许被改变的。例如让指针指向其他地址空间,是不被允许的 */
int * const p;

/* 下面这两种写法意思一样。就是结合了上面的两种情况,指针p的指向不允许改变,指向的内存空间的值也不允许改变 */
int const * const p;
const int * const p;

A reading tip : After removing the type (such as the above int type), then see which symbol is modified closest to const, such as modifying the symbol * first, which means that the content of the memory space pointed to by the pointer is not allowed to be changed, such as: const int *p;. const first modifies the pointer variable itself, then it means that the modified pointer is not allowed to change the pointed address, for example:int * const p;

3. Can variables modified by const really not be changed?

As mentioned earlier, the read-only attribute of const-modified variables in C language is realized by the compiler through syntax checking. That is to say, when we write code, we can fool the compiler and indirectly modify the value of const variables. .

3.1 The case of const modifying local variables in C language

#include <stdio.h>

int main(void)
{
    
    
    /* 定义一个const局部变量 */
	const int a = 10;
	int *p = (int *)&a;
    
	*p = 20;
	
	printf("a = %d.\n", a);
	printf("*p = %d.\n", *p);

	return 0;
}

After compiling and running, the output is as follows:

insert image description here

Obviously, the value of the variable a is changed to 20, indicating that in the C language, const-modified local variables can be changed. This is because local variables modified by const are stored on the stack, and as long as the compiler is fooled, they can be changed indirectly.

3.2 The case of const modifying global variables in C language

In C language, const modified global variables, if we try to indirectly change the value of the global variables, it will cause the program to crash.

First look at the following code:

#include <stdio.h>

/* 定义一个const的全局变量 */
const int g_a = 1;

int main(void)
{
    
    
	int *p = (int *)&g_a;
    
	*p = 5;
	
	printf("a = %d.\n", a);
	printf("*p = %d.\n", *p);

	return 0;
}

After compiling and running, a segmentation fault occurred. as follows:

insert image description here

The reason is because the global variables modified by const are stored in the read-only global data area (read-only-data). Since it is stored in the read-only global data area, if you want to modify it, it will only cause The program crashed.

Of course, for older C language compilers, const-modified global variables are not placed in read-only access areas, but are also placed in readable and writable storage areas, such as bcc compilers.

4. The evolution of the const keyword in C++

In C++, on the basis of inheriting all the features of const in C language, the following features are added:

  • When a variable declared by const is encountered, the variable symbol and corresponding value will be put into the symbol table of the compiler;
  • During the compilation process, if the variable modified by const is found, the value is directly taken out from the symbol table and replaced;
  • During compilation, if any of the following situations are found, storage space will be allocated for const-modified variables:
    • The address operator is used for const-modified variables, namely &;
    • When the const variable is a global variable and needs to be used in other files (that is, it is modified with the extern keyword).

Note : Although the C++ compiler may allocate memory space for the variable modified by const, when we use the variable in the code, we will not go to the storage space of the variable to get the value, but the compiler directly from its symbol constant table find the corresponding value to replace. We verify this in code:

#include <stdio.h>

int main(void)
{
    
    
    /* 定义一个const局部变量 */
	const int c = 0;
	int *p = (int *)&c;
    
	printf("Begin...\n");
	
	*p = 5;
	
	printf("c = %d.\n", c);
	
	printf("End...\n");

	return 0;
}

Compilation and running results are as follows:

insert image description here

It can be seen that the value of the printed variable c is still the initial value when it was defined, but we obviously changed the value of the storage space of the variable c indirectly through the pointer p. Why is the printed value of c still 10?

This is what was mentioned above. In C++, for variables modified by const, C++ will put the variable c into the symbol constant table. When c is found to be used (such as printing the value of c here), it will be found from the constant table. The value of c is replaced. The general process is shown in the figure below:

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-TqULG3CA-1662197864032)(../picture/image-20220903150825426.png)]

Therefore, we indirectly changed the value of the storage space of the variable c through the pointer, but in C++, when the variable a is used, it does not use the value of the storage space of a, but uses the value in the symbol constant table.

In other words, in C++, variables modified by const can define constants in the true sense.

5. const and #define macro definition

In C++, we see variables modified by const. When using this variable, it is directly replaced from the symbol constant table, so isn't this equivalent to a macro definition?

Although const constants in C++ are indeed very similar to macro definitions, they are not exactly equal to macro definitions because:

  • const constants are handled by the compiler
  • The compiler can perform type checking and scope checking on const constants
  • Macro definitions are processed by the preprocessor, just for text replacement

The following code example:

#include <stdio.h>

void func1(void)
{
    
    
	#define a 3
	const int b = 4;
}

void func2(void)
{
    
    
	/* 宏没有作用域的概念,不会报错 */
	printf("a = %d\n", a);
	
	/* 会报错,因为const修饰的b是有作用域的 */
	printf("b = %d\n", b);
}

int main(void)
{
    
    
    /* 定义一个const局部变量 */
	const int A = 1;
	const int B = 2;
	
	/* C++中,这样定义数组是被允许的,因为是进行常量替换
	 * 而在C语言中是不允许的, 因为这时编译器根本不知道要分配多少空间给该数组
	 */
	int array[A + B];	
	
	return 0;
}

After compiling, the following error will be reported:

insert image description here

The error said that b was not declared within the scope of the function, indicating that there is still a difference between const and macro.

6. The magical use of const in function parameter passing in C language

We know that when passing parameters in a function, if you want to modify the value of the external variable corresponding to the function parameter when the function parameter is changed inside the function, you must pass the address (pointer) of the variable as the function parameter.

Like the following example:

void swap(int *a, int *b)
{
    
    
	int temp;
	
	temp = a;
	a = b;
	b = temp;
}

A classic function for exchanging the values ​​of two variables, passing a pointer as a function parameter, can actually change the value of the memory space pointed to by the pointer, and realize the exchange of the values ​​of the two variables. In fact, the function parameter is used as an output parameter here , which means that the value after the exchange of two variables is returned to the caller through the function parameter.

But many times, when we pass in a pointer as a function parameter, we don't want to change the content of the memory space pointed to by the pointer. We just want to use the content of the memory space pointed to by the pointer to perform code operations. Because if a variable occupies a large memory space (such as defining a large structure variable), in order to save the stack memory overhead in the process of function parameter passing, the pointer is passed as a function parameter.

For example, the implementation of the strlen function:

size_t strlen(const char *str)
{
    
    
	const char *sc;

	for (sc = str; *sc != '\0'; sc++);

	return sc - str;
}

The function parameter is a pointer of char type modified by const. In this function, in fact, we just want to use the content pointed to by this pointer, and have no intention of modifying its content, so if we add const modification, we can prevent us from accidentally modifying the pointer inside the function The contents of the memory space pointed to by the variable.

In other words, when we use a pointer as a function parameter, without adding const modification, the information that can be passed to the programmer is that the function parameter is used as an output parameter, that is, we want to change the pointer parameter inside the function. The contents of the memory space pointed to. And if you add const to modify the pointer variable, you can pass a message to the programmer that the content of the memory space pointed to by the pointer is not allowed to be changed, and this function just wants to use the memory space pointed to by the pointer content only.

In the kernel source code of Linux, or in some system APIs provided by Linux, this convention is followed. When pointers are used as function parameters, some useful information can be passed to programmers when they are modified with const.

7. Summary of the role of const

  1. Variables modified by const can use the compiler to help us perform syntax checks to prevent us from unintentionally modifying parameters that we do not want to be changed during the process of writing code.

  2. When passing parameters in a function, the pointer variable modified by const can pass some application purposes of the pointer parameter to the programmer, such as whether it is used as an output parameter or just uses the content of the memory space pointed to by the pointer.

  3. Using the keyword const to modify variables can provide the compiler with some information to optimize the code, which may compile and generate more compact code.

Guess you like

Origin blog.csdn.net/luobeihai/article/details/126680305