[C + +] section III - detailed introduction

Posted by summoner on Wed, 05 Jan 2022 23:28:26 +0100

Reference - alias (&)

Type name& Reference variable name (object name)=Reference entity;

Instead of defining a new variable, a reference gives an alias to an existing variable. The compiler will not open up space for the referenced variable. It shares the same memory with its reference

characteristic

  • References must be initialized when defined
  • A variable can have multiple references
  • Once an entity is referenced, it cannot reference other entities

Often cited

void Test()
{
   const int a = 10;
   //int& ra = a;     An error will be reported when compiling this statement, because a is defined as const and type is constant. Use a common type RA to refer to a, RA represents modifiable, and a is not safe
   const int& ra = a;//Yes, even if it is referenced, it cannot be modified
   //int& b = 10;    // There will also be errors. B is a constant
   const int& b = 10;//correct
   double d = 12.34;
   //int& rd = d;     An error is reported when compiling this statement. The reference type must be the same type as the reference entity
   const int& rd = d;//Yes, the const type reference cannot be modified. It only takes the integer part of d
}

💙 Let's see something interesting

int a = 10;
const int& ra = a;
//ra = 100;// An error will be prompted because it is a const type reference and cannot be modified
a = 200;//It can be modified. ra shares a memory space with A. when a changes, ra also changes

It can be seen that the addresses of ra and a are the same. Although the value of a cannot be modified through ra, ra will change together after a modifies the value

💙 Let's look at the following

double d = 12.34;
const int& rd = d;
d = 56.78;


Assuming that rd is a reference to d, when d becomes 56.78, rd should also be changed. However, we actually use debugging and find that rd does not change after d is changed, and their addresses are different

📍 Why?

According to the characteristics of variables, we know

1. The types of reference variables and reference entities must be consistent

2. Implicit conversion can occur between double and int types

When rd refers to d, the compiler finds that the type of rd is inconsistent with that of d, so the compiler creates a temporary space as a transition to let rd refer to the temporary space created by the compiler

Suppose we can't see the address of rd from the monitoring window

📍 Do you know the address of the temporary space created by the compiler?

I don't know

📍 Do you know the name of the temporary space created by the compiler?

I don't know

📍 Since you don't know the name and address of the temporary space, can you modify the temporary space?

may not

Conclusion: the temporary space created by the compiler is constant ----- > so we can't modify it

Referenced application scenarios

1. Simplify code writing

Note that typedef aliases the type and & aliases the variable

struct A
{
    int a;
    int b;
    struct B
    {
        int c;
    };
    B stuB;
        
};
int main()
{
    A stuA;
    //When you don't use references, you need to write Stua every time you want to access C stuB. c
    stuA.stuB.c = 10;
    stuA.stuB.c = 20;
    stuA.stuB.c = 30;
    //Let's take a look at using references
    int& rc = stuA.stuB.c;
    rc = 40;
    rc = 50;
    //Is it convenient~
}
2. Reference type as function parameter
//Use pointer
void Swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main()
{
    int a = 10;
    int b = 20;
    Swap(&a, &b);
    return 0;
}
//Use reference
//It can basically replace the first level pointer in C language
void Swap(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}
int main()
{
    int a = 10;
    int b = 20;
    Swap(a, b);
    return 0;
}
3. Reference as function return value
int& Add(int left, int right)
{
   int ret = left + right;
   return ret;
}
int main()
{
    //result is the alias of the ret local variable in the Add function
    int& result = Add(1, 2);//result=3
    Add(3, 4);//result=7
    Add(5, 6);//result=11
    return 0;
}

📍 Does the above code look a little strange? Why does the value of result change?

1. First, we use the monitoring window to find that the addresses of result and ret are the same

In fact, the result actually refers to the ret local variable in the body of the Add function. When the Add function runs, the space of the ret local variable in the function is recycled, and the result actually refers to an illegal space

2. If you want to find out completely, you need to sort it out from behind the function call

When each function runs, it needs to have its own independent stack memory time, which is called the stack frame used at the time of function call

This block of memory space is applied for as the function is called and recycled as the function ends

esp and ebp are two registers to mark the top and bottom of the stack frame

3. After the add function runs, it does not clean up the garbage data left in the stack frame. result is the alias of ret, so you can see the garbage data in the space

So we need to pay attention to:

📍 Must not return the space on the function stack ----- > typical: local variable

After the function is completed, the local variables inside the function body are destroyed. If the external reference receives the return value of the function by reference, the external reference actually refers to an illegal memory space

📍 What kind of variable entities can we return?

As long as the returned entities and variables are not destroyed by the end of the function, such as global variables, local static variables, and variables of reference type

Pointer and reference commonalities

In terms of syntax concept, reference is an alias without independent space, but in fact, the underlying implementation of reference is implemented in the form of pointer. From the bottom, reference is a pointer, and reference actually has space, because reference is a pointer, and the address of the reference entity is stored in it

Pass referenced assembly code

Assembly code of transmission address


📍 Doesn't it mean that the compiler won't make room for variables of reference type?

In fact, the reason for saying this conceptually is to better understand references, but the underlying implementation technology of references is pointers

📍 Then why do you need to quote?

Then we need to talk about the differences between pointers and references in concepts, features, and applications

Supplement:

The pointer is 8 bytes not because the operating system is 64 bits, but because it is compiled in a 64 bit manner

Pointer and reference are different

1. The reference must be initialized during definition, but the pointer is not required

2. After initializing and referencing an entity, the reference cannot reference other entities, and the pointer can point to any entity of the same type at any time

3. There is no NULL reference and a NULL pointer

4. The meaning in sizeof is different. The result of reference is the size of the reference type, but the pointer is always the number of bytes (4 / 8 bytes) occupied by the address space

5. Reference self addition means that the referenced entity is added by one, and pointer self addition means that the pointer is offset backward by one type size

6. There are multi-level pointers and no multi-level references

7. There are different ways to access entities. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself

8. References are safer to use than pointers

💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙 It's over 💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙

Topics: C++ Back-end