Research on Pointers and references in C and C++

  • 2020-04-02 01:48:07
  • OfStack

Basic knowledge
Declaration of Pointers and references:
Declaration pointer: char* PC;
Declaration reference: char c = 'A'
    char & Rc = c;

Their differences:
(1) from the phenomenon that the pointer can change the value it points to at run time, and once the reference is bound to an object, it will not change. Pointers can be reassigned to point to a different object. The reference, however, always points to the object specified at initialization and cannot be changed later, but the contents of the specified object can be changed.

From the point of view of memory allocation, the program for pointer variables to allocate memory area, but not for reference to allocate memory area, because the reference declaration must be initialized, so as to point to an existing object. A reference cannot point to a null value.

From the compilation point of view, the program in the compilation of Pointers and references will be added to the symbol table, the symbol table is the variable name and the corresponding address of the variable. The address value of the pointer variable on the symbol table is the address value of the pointer variable, and the address value of the reference on the symbol table is the address value of the reference object. The symbol table is not changed after it is generated, so the pointer can change the object it points to (the value in the pointer variable can be changed), and the reference object cannot be changed. This is the main reason why using Pointers is not safe and using references is safe. In a sense a reference can be considered an immutable pointer.

The fact that there are no references to null values means that the code using references is more efficient than using Pointers. Because you don't need to test the validity of a reference before you use it. Instead, Pointers should always be tested to prevent them from being null.

Theoretically, there is no limit to the series of Pointers, but the reference can only be one level.

As follows:


  int** p1; //Legal. Pointer to a pointer
  int*& p2; //Legal. A reference to a pointer
  int&* p3; //Illegal. Pointers to references are illegal
  int&& p4; //Illegal. A reference to a reference is illegal

Notice that this is pronounced from left to right.

Procedure 1:

#include "stdio.h"
int main(void)
{
  //Declare a char pointer PC and have it point to a null value
  char* pc = 0;
  char a = 'a';
  //Declare a reference to rc and have it refer to variable a
  char& rc = a;
  printf("%d, %c\n", pc, rc);

  char *pc2;
  //Declare a pointer, but do not initialize it
  pc2 = pc;

  // char& rc2;
  //The following error occurs when the above statement is compiled:
  // error C2530: 'rc2' : references must be initialized
  //That is, the application must be initialized
  // rc = *pc;
  //The above statement will compile without problems, but at runtime, the following error will be reported:
  //The "0x00401057" instruction refers to the "0x00000000" memory. This memory cannot be "read"
  //Indicates that a reference cannot in any case point to a null value

  return 0;
}

Program 2:

#include <iostream>
#include <string>
using namespace std;
int main(void)
{
  string s1("Hello");
  string s2("World");
  //Printf (" % s \ n ", s1); Instead of printf printing s1, you should use cout

  cout << "s1 The address of the  = "<< &s1 << endl;// &s1 = 0012FF64
  cout << "s2 The address of the  = "<< &s2 << endl;// &s2 = 0012FF54

  string& rs = s1;   //1. Define a reference rs, rs reference s1
  cout << " reference rs The address of the   =  " << &rs << endl;  // &rs = 0012FF64

  string* ps = &s1; //Define a pointer, ps, to s1
  cout << " Pointer to the ps The address of the  = " << ps << endl;// ps = 0012FF64

  cout << rs << ", " << *ps << endl;  // Hello, Hello
  //If there is no #include <String> , the following error occurs when the above statement is compiled:
  // error C2679: binary '<<' : no operator defined which takes a right-
  // hand operand of type 'class std::basic_string<char,struct
  // std::char_traits<char>,class std::allocator<char> >'
  // (or there is no acceptable  conversion)

  rs = s2;  //2. Rs still references s1, but s1 is now "World"
  ps = &s2;   //Ps is now pointing to s2

  cout << " reference rs The address of the   =  " << &rs << endl;  //& amp; Rs = 0012FF64 unchanged
  cout << " reference rs The value of the   =  " << rs << endl;   //Rs = "World" has changed

  cout << " Pointer to the ps The address of the   =  " << ps << endl;//Ps = 0012 ff54 & have spent Has changed
  cout << " Pointer to the ps The content of the address referred to   =  " << *ps << endl;  //*ps = World has changed

  cout << "s1 The address of the  = "<< &s1 << endl;//3. & amp; S1 = 0012FF64 unchanged
  cout << "s1 The value of the  = " << s1 << endl; //4. S1 = World  Has changed

  return 0;
}

Consider:
A reference is an alias name for a variable that is determined when the reference is initialized and cannot be changed later. See program 2's bold word statement. The first sentence states that rs refers to s1, and that s1 has a value of "Hello". From then on, rs is actually equivalent to the variable s1, or more fundamentally, the address of rs is the address of s1 when it was initialized and will not be changed. It should be easy to understand, for example, if we define a variable a in our program, no matter how we assign a value to a, its address will not change;

In the second sentence, rs still refers to the address of s1 at the time of initialization, but the assignment here is equivalent to re-assigning s1, so we can see from the third and fourth sentences that the address of s1 has not changed, but the value has changed.

Pass as a parameter
This feature of the reference can be used as an outgoing argument to a function. As in procedure 3:


#include <iostream>
#include <string>
using namespace std;
int newEvaluation(string& aStr)
{
  string bStr("Hello,");
  aStr = bStr + aStr;

  return 0;
}

int main(void)
{
  string aStr("Patrick!");
  newEvaluation(aStr);
  std::cout << aStr << endl; //Output: "Hello, Patrick!"

  return 0;
}

General variables, however, cannot be passed from inside functions, such as program 4:

#include <iostream>
#include <string>
using namespace std;

int newEvaluation(string aStr)
{
  string bStr("Hello,");
  aStr = bStr + aStr;

  return 0;
}

int main(void)
{
  string aStr("Patrick!");
  newEvaluation(aStr);
  std::cout << aStr << endl; //Output: "Patrick!" , the aStr value does not change

  return 0;
}

Of course, the way of passing reference in program 3 can also be written as the way of passing pointer, such as program 5:

#include <iostream>
#include <string>
using namespace std;

int newEvaluation(string* const aStr)
{
  string bStr("Hello,");
  *aStr = bStr + *aStr;

  return 0;
}

int main(void)
{
  string aStr("Patrick!");
  newEvaluation(&aStr);
  std::cout << aStr << endl; //Output: "Hello, Patrick!"

  return 0;
}

Note the pitfalls in the program, such as program 6:

#include <iostream.h>
int *pPointer;
void SomeFunction()
{
  int nNumber;
  nNumber = 25;
  //I'm going to point to nNumber
  pPointer = &nNumber;
}

void main()
{
  SomeFunction();//For pPointer assignment
  //Why did it fail here? Why didn't I get 25
  cout << "Value of *pPointer: " << *pPointer << endl;
}

This program first calls the SomeFunction function, creates a variable called nNumber, and then lets the pPointer point to it. But what's the problem? When the function ends, the nNumber is deleted because of this local variable. Local variables are automatically deleted by the system after their functions are executed. That is, when the SomeFunction returns the main function main(), the variable has been deleted, but the pPointer also points to the area where the variable was used but is no longer part of the program.

Although you use what's called dynamic memory allocation in SomeFunction. Trap also exists in procedure 7:

#include <iostream.h>
int *pPointer;

void SomeFunction()
{
int intNumber = 25;
//Point to a new integer
pPointer = new int;
pPointer = &intNumber;
}

void main()
{
SomeFunction();   //For pPointer assignment
cout<< "Value of *pPointer: " << *pPointer << endl;
delete pPointer;
}

The reason is also as above, intNumber scope is limited to the SomeFunction, leave the SomeFunction, then intNumber does not exist, so & IntNumber, the address of the intNumber, becomes meaningless, so the value to which the address refers is uncertain. There will be no problem if you change to the following program.

Program 8:


#include <iostream.h>
int *pPointer;

void SomeFunction()
{
int intNumber = 25;
//Point to a new integer
pPointer = new int(intNumber);
}

void main()
{
SomeFunction();   //For pPointer assignment
cout<< "Value of *pPointer: " << *pPointer << endl;
delete pPointer;
}

Three, the pointer pointer
As we said before, Pointers are not limited by series.
Program 9:

#include<stdio.h>
#include<stdlib.h>

void main(void)
{
int i, j;
int a[10], b[3][4], *p1, *p2, **p3;   
for(i = 0; i < 10; i++)
   scanf("%d", &a[i]);   

for(i = 0; i < 3; i++)
   for(j = 0; j < 4; j++)
   scanf("%d", &b[i][j]); 

p1 = a;
p3 = &p1;
for(i = 0; i < 10; i++)
   printf("%4d", *(*p3+i));
printf("\n");

for(p1 = a; p1 - a < 10; p1++)
{
   p3 = &p1;
   printf("%4d", **p3);
}
printf("\n");

for(i = 0; i < 3; i++)
{
   p2 = b[i];
   p3 = &p2;
   for(j = 0; j < 4; j++)
   printf("%4d",*(*p3+j));
   printf("\n");
}

for(i = 0; i < 3; i++)
{
   p2 = b[i];
   for(p2 = b[i]; p2-b[i] < 4; p2++)
   {
   p3 = &p2;
   printf("%4d", **p3);
   }
   printf("\n");
}
}

Output results:
1     2     3     4     5     6     7     8     9     10
1     2     3     4     5     6     7     8     9     10
11   12   13   14
15   16   17   18
19   20   21   22
11   12   13   14
15   16   17   18
19   20   21   22

Function Pointers and function references
Function Pointers are one of C++ 's greatest strengths. Advanced programmers are more likely to use references whenever possible than regular Pointers because they are easier to handle. However, when working with functions, function references do not necessarily have this advantage over function Pointers. Existing code makes little use of function references. The following sections show you how to use function Pointers, how to use function references, and where to use them.

Function pointer example


#include <iostream>
void print(int i)
{
std::cout << i << std::endl;
}

void multiply(int& nDest, int nBy)
{
nDest *= nBy;
}

void print_something()
{
std::cout << "something" << std::endl;
}

int sayHello()
{
std::cout << "Hello, World!" << std::endl;
return 10;
}

int main()
{ 
void (*pFunction_1)(int);
pFunction_1 = &print;
pFunction_1(1);
//The output is 1

void (*pFunction_2)(int&, int) = &multiply;
int i = 1;
pFunction_2(i, 10);
std::cout << "i = " << i << std::endl;
//The output is 10

void (*pFunction_3)();
pFunction_3 = &print_something;
pFunction_3();
//The output is something

int (*pFunction_4)();
pFunction_4 = &sayHello;
int a = pFunction_4();
//The output is Hello, World!
std::cout << a << std::endl;
//The output is 10

return 0;
}

Examples of function references

#include <iostream>
void print(int i)
{
std::cout << i << std::endl;
}

void print2(int i)
{
std::cout << i << std::endl;
}

void multiply(int& nDest, int nBy)
{
nDest *= nBy;
}

void print_something()
{
std::cout << "something" << std::endl;
}

int sayHello()
{
std::cout << "Hello, World!" << std::endl;
return 10;
}

 
int main()
{  
// void (&rFunction_1)(int);
//Error: uninitialized reference! The reference must be initialized

void (&rFunction_2)(int) = print;
rFunction_2(1);
//Output 1

rFunction_2 = print2;
rFunction_2(2);
//The output of 2

void (&rFunction_3)(int&, int) = multiply;
int i = 1;
rFunction_3(i, 10); 
std::cout << i << std::endl;
//Output 10

void (&rFunction_4)() = print_something;
rFunction_4();
//Output something

int (&rFunction_5)();
rFunction_5 = sayHello;
int a = rFunction_5();   //Output the Hello, World!
std::cout << a << std::endl;
//Output 10

return 0;
}

Function pointer and function reference as function parameters

#include <iostream>

void print(int i)
{
std::cout << i << std::endl;
}

void print2(int i)
{
std::cout << i * 2 << std::endl;
}

void printSomething()
{
std::cout << "Something" << std::endl;
}

void sayHello()
{
std::cout << "Hello, World!" << std::endl;
}

void call_p_func(void (*func)(int))
{
func(1);
func(2);
func(3);
}

void call_r_func(void (&func)(int))
{
func(1);
func(2);
func(3);
}

void call_p_function(void (*func)())
{
func();
}

int main()
{ 
std::cout << " Function Pointers as arguments " << std::endl;
call_p_func(&print);
call_p_func(&print2);
call_p_function(&printSomething);
call_p_function(&sayHello);
call_p_function(sayHello);
//The above two sentences are the same for some compilers, but the former is recommended,
//This makes the program more readable

std::cout << " Function references as arguments " << std::endl;
call_r_func(print);
call_r_func(print2);

return 0;
}

Conclusion:
Declaration of function pointer:
< The return type of the function you want to point to > (* function pointer name) < The parameter type of the function you want to point to... >
To declare a function pointer to the following function:

void print(int i)
{
std::cout << i << std::endl;
}

Then you can do the following:
Void (* pFunction) (int);
Then assign the pFunction to the address of the function as follows:
PFunction = & The print;
And then, pFunction can be used just like print, for example,
PFunction (1);
And so on.

Declaration and usage of function references:
< The return type of the function to be referenced > ( & Name of function reference) < Parameter type of the function to be referenced... > = < The name of the function to reference > So, the reference must be initialized when declared, and the reference cannot point to a null value.
To declare a function reference to the following function:

void print(int i)
{
std::cout << i << std::endl;
}

Then you can do the following:
Void ( & RFunction) (int) = print;
Then, rFunction can be used just like print, for example,
RFunction (1);
And so on.

Const modifies Pointers and references
Generally speaking, there are three cases of const modified pointer and reference, namely, const modified pointer, const modified reference and const modified pointer reference. They are discussed below.

Const modifies pointer
Const modifier pointer can be divided into three cases: const modifier pointer itself, the variable (or object) referred to by const modifier pointer, and const modifier pointer itself and the variable (or object) referred to by const modifier pointer.

A. const modifies the pointer itself
In this case, the pointer itself is constant and cannot be changed, and any modification of the pointer itself is illegal, for example:
Double PI = 3.1416;
Double * const PI = & PI;

Double alpha = 3.14;
PI = & Alpha;     / / error. Because the pointer PI is constant, it cannot be changed.
* PI = alpha;     / / OK. Although the pointer PI cannot be changed, the variable or object that the pointer refers to is mutable.

B. const modifies the variable (or object) to which the pointer points
In this case, the pointer itself can be changed, but the object to which the const modifies pointer cannot be changed, for example:
Double PI = 3.1416;
Const double * PI = & PI;

Double alpha = 3.14;
* PI = alpha; / / error. Because what PI points to is constant, *PI cannot be changed.
PI = & Alpha; / / OK. Although the contents of the pointer cannot be changed, the pointer PI itself can be changed. So you can change *PI in this way.

C. Const modifies the pointer itself and the variable (or object) to which the pointer refers
In this case, neither the pointer itself nor the variable (or object) to which the pointer refers can be changed, for example:
Double PI = 3.146;
Const double* const PI = & PI;
//double const* const PI = & PI;
cout < < "PI =" < < PI, < < Endl;
cout < < "* PI =" < < * PI < < Endl;

Double alpha = 3.14;
/ / * PI = alpha; / / error. Because what PI points to is constant, *PI cannot be changed.
/ / PI = & Alpha; / / error. Because the pointer PI is constant, it cannot be changed.

Const modifies the reference
A const decorated reference is not as complex as a pointer decorated pointer and has only one form. The reference itself cannot be changed, but the object to which it refers can be changed. See "basics" above.
Double PI = 3.1416;
/ / const double & PI = PI;
Double const & PI = PI;   // is equivalent to the above sentence
/ / double & Const PI = PI; // there is a problem. Many compilers produce warnings
cout < < PI, < < Endl;

Const modifies pointer reference
Let's use an example.
Double PI = 3.14;
Const double * pPI = & PI;
/ / const double * & The rPI = & PI; / / error. Cannot convert double* to const double* &
Const double * & The rPI = pPI;     / / OK. Declare the correct method of a pointer reference

Description: const double* & The rPI = & PI; Why did it go wrong? We know that a reference is an alias for the referenced object, and because of that, since rPI is an alias for pPI, the types of rPI and pPI must be exactly the same. As you can see from the code snippet above, the rPI is of type const double*, and & The type of PI is double*, so this sentence is incorrect.

The following code corresponds to the situation in b in (that is, the content is immutable, the pointer is variable) :
Double PI = 3.1416;
Double API = 3.14;
Const double * pPI = & PI;
Const double * pAPI = & API.
Const double * & The rPI = pPI;
Const double * & RAPI = pPI;

* rAPI = API. / / error. The value of the pointer cannot be changed directly
RAPI = pAPI;     / / OK. The pointer itself can be changed

There are other USES of pointer references that are too rare to mention.


Related articles: