C + + - the essence of reference, the relationship between const and pointer, and the relationship between const and reference

Posted by dscapuano on Thu, 13 Jan 2022 10:07:28 +0100

Nature of reference

For the following program (including after compilation):

void fun(int &a)   //--> void fun(int * const a)
{
	int *p = &a;  //-->int *p = a;//int *p = *&a;
	a = 100;      //-->*a = 100
	*p = 200;
}
int main()
{
	int x = 10;
	int &y = x;  //-->int * const y = &x;
	fun(x);      //-->fun(&x);
	fun(y);
	return 0;
}

Thus: the essence of reference is implemented in C + +, and it is a constant pointer. The C + + compiler uses constant pointers as the internal implementation of references in the compilation process, so the space occupied by references is the same as that of pointers. This process is an internal implementation of the compiler and is invisible to users.

Therefore, we can understand the reason why the reference needs to be initialized: if an int &a is defined, it is explained at the bottom of the compiler that it is int * const a. at this time, a is a random value. If its direction is not clear, it is equivalent to a "wild pointer" and cannot be modified. Therefore, variables that are constant must be initialized during definition.

Why do we prefer references to pointers?

Because the reference is safer, and the reference is a syntax sugar of the pointer, when using the reference, we do not need to judge null like the pointer, because there is no null reference, and the program code is safer.

At the same time: we do not allow the reference of local variable to return, because after returning the address of local variable a, give the value of & A to the dead value (intermediate variable), and the relevant stack frame resources of fun function will be recycled. A no longer exists. At this time, the main function can not find the value of a (invalid pointer) when it dereferences it, even if it can get the correct value sometimes, We also don't approve of this writing method, because we can't get this value from the expired space (unsafe).
See the following procedure:

int& fun() //-->int * const fun()
{
	int a = 10;
	return a; //-->return &a;
}
int main()
{
	int x = fun();
	int& y = fun(); //-->int y = *fun();
	
	cout<<x<<" "<<y<<endl;
	return 0;
}

Give a program and analyze the underlying reasons why they have different execution results?

int *fun()
{
	int arr[10] = {12,23,34,45,56,67,78,89,90,100};
	return arr;
}
int main()
{
	int *p = fun();
	for(int i = 0;i < 10;i++)
	{
		printf("%p => %d\n",p,*p);
		p+=1;
	}
	return 0;
}

In the execution result of the above program, the print results of each value in the array are random values
The reason is that the stack frame space has been recovered by the system. After that, calling the printf() function will occupy the space before storing the array arr, which is equivalent to clearing the value of the array remaining in that space. We visit the invalid space and print the random value naturally.

However, if you change the data size to 100 But I found that it can print normally. What's the reason?
For the array arr with 1000 elements, we need to open up a larger stack frame space (from low address to high address). After returning the ARR address, p points to this space and calls the printf() function. The occupied space will not interfere with the remaining space for storing the first ten values of the array, that is, that space is not allocated to the printf function, That space is not filled, so the remaining values can still be printed normally.
This can explain why normal results can sometimes be obtained when returning with a reference to a local variable.

summary

When can the value of a variable be returned as a reference?
————————The lifetime of this variable is not affected by the lifetime of the function (for example, the variable is a global variable or a static local variable [. data], and the variable entering the function with reference as a formal parameter continues to return in the form of reference)

Relation between const and pointer

Next, we will sort out the relationship between const and pointer through a few pieces of code

int a = 10;

int* p1 = &a;

int const * p2 = &a;
const int *p2 = &a;

int * const p3 = &a;

const int * const p4 = &a;

Since there is no constant constraint on variable a in the above code, subsequent codes can be compiled.
Let's look at the following code:

int a = 10;
const int* s = &a;

int* p1 = s;
int const * p2 = s;
int * const p3 = s;
const int * const p4 = s;

Although the pointer variable s in the above program can point to a, due to the constant constraint, the value of a cannot be changed through * s, so p1 and p3 cannot be compiled, because in this process, the value of variable * s can be modified through * p1 and * p3, thus modifying the value of variable a, which violates the constant limit of * s.

int a = 10;
int* cosnt p = &a;

int *s0 = p;//1
const int *s1 = p;//2
int *const s2 = p;//3
const int * const s3 = p;//4

The above four statements can be compiled because although the value of the variable p cannot be changed, even if the value of s0 itself is changed, it will not lead to the change of the value of P itself. In this process, the right is not expanded.

Analyze the results of the following procedures:

const int a = 10;
int b = 0;
int* p = (int *)&a;//The reason for type strong conversion here is that a is constant and cannot be pointed to through an ordinary pointer variable, otherwise the value of a can be changed through the pointer variable
*p = 100;
b = a;
cout<<"a = "<<a <<"b = "<<b << "*p = "<<*p <<endl;

Your answer may be:
a = 100 b =100 *p = 100

But the real result is:
a = 10 b =10 *p = 100
The reason is that the const modified constant variable has been replaced with the following form in the compilation process before running:

const int a = 10;
int b = 0;
int* p = (int *)&a
//There is also a kind of "go to the regular strong turn", which is equivalent to the previous sentence
int *s = const_cast<int *>(&a);
*p = 100;
b = 10;
cout<<"a = "<<10 <<"b = "<<b << "*p = "<<*p <<endl;

Thus, in the C + + compilation mode, constant variables and macros are replaced in the pre run compilation process.

Relation between const and reference

As before, we will sort out the relationship between const and reference through a few pieces of code

int a = 10;
int &b = a;//ok

const int a = 10;
int &b = a;//error
const int &b = a;//ok

int &c = (int&)a;//????

For a constant variable a, if a common reference is used, the value of a can be changed through the reference. Therefore, this writing method cannot be compiled. Therefore, it is also necessary to add a constant limit to the reference, which is the same as the pointer

int a = 10,b = 20;

int *s = &a;

int *&p1 = s;

const int *&p2 = s;

int * const &p3 = s;

const int * const &p4 = s;

In the above statement:

  1. p1 is a pointer type reference, that is, the alias of S. therefore, the value of p1 itself can be changed. At this time, the value of s will also be changed, or the value of a can be changed through * p1
  2. p2 is also an alias of S. however, since the pointing modification ability of p2 is constrained, you cannot modify the value of a through * p2, but you can modify p2 = & B or S = & B (p2 changes [alias]). At this time, the operation results * p2 and * s in both cases are 20
  3. p3 is also an alias of s. you can change the value of a through * p3. If you change the value of s, p3 will follow the change, that is, you can change the value of s and the value of b through * p3, but the value of p3 itself cannot be changed
int a = 10,b = 20;

const int *s = &a;

int *&p1 = s;

const int *&p2 = s;

int * const &p3 = s;

const int * const &p4  = s;

p1 and p3 cannot be compiled. This is very simple. Let's look at the following program:

int a = 10,b = 20;

int * const s = &a;

int *&p1 = s;

const int *&p2 = s;

int * const &p3 = s;

const int * const &p4  = s;

p1 and p2 failed to compile,

  1. Because if you change the value of p1 itself, the value of s will also change, which is contrary to the constant constraint of S itself
  2. const in the second sentence modifies the pointing ability of p2, so the value of p2 itself can be changed, which will also produce contradiction
  3. There will be no contradiction between the last two sentences.

Topics: C C++