常引用、常量指针、指针常量、指向常量的常指针、空指针与野指针解释

1、一.基础知识

  • 引用并非对象
  • 引用必须初始化
  • 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起
  • 类型要严格匹配
int &a = 10;             //错误:引用类型的初始值必须是一个对象  
  
double a = 3.14;  
int &b = a;              //错误:此处引用类型的初始值必须是int型对象
  • 指针本身就是对象
  • 指针的类型要和它指向的对象严格匹配
double dval;  
double *pd = &dval;      //正确  
double *pd2 = pd;        //正确  
  
int *pi = pd;            //错误:指针pi的类型和pd的类型不匹配  
pi = &dval;              //错误:试图把double型对象的地址赋给int型指针  

二.常量引用

  • 初始化常量引用时允许用任意表达式作为初始值
int i = 42;  
const int &r1 = i;       //正确:允许将const int & 绑定到一个普通int对象上  
const int &r2 = 42;      //正确  
const int &r3 = r1 * 2;  //正确  
int &r4 = r1 * 2;        //错误  
  
double dval = 3.14;  
const int &ri = dval;    //正确  
//等价于  
const int temp = dval;  
const int &ri = temp;  
  
int i = 42;  
int &r1 = i;  
const int &r2 = i;  
r1 = 0;                  //正确  
r2 = 0;                  //错误  

三.常量指针

  • 定义: 又叫常指针,可以理解为常量的指针,也即这个是指针,但指向的是个常量,这个常量是指针的值(地址),而不是地址指向的值。
  • 关键点:
    • 1.常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改;
    • 2.常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值;指针指向的地址可以改变,但指针指向的地址所对应的内容不可以改变
    • 3.指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址;
  • 代码形式:
    int const* p;  const int* p;
    

四.指针常量

  • 定义:本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
  • 关键点:
    • 1.指针常量的值是指针,这个值是常量,不能被修改;指向的地址不可以改变,但指向的地址所对应的内容可以改变
    • 2.指针本身是常量,指针指向的地址不可以变化,但是指针指向的地址所对应的内容可以变化;
  • 代码形式:
    int* const p;
    

五.指向常量的常指针

  • 定义:指向常量的指针常量就是一个常量,且它指向的对象也是一个常量。
  • 关键点:
    • 1.一个指针常量,指向的是一个指针对象;
    • 2.它指向的指针对象且是一个常量,即它指向的对象不能变化;
  • 代码形式:
    const int* const p;
    

六.那如何区分这几类呢?

  • 带两个const的肯定是指向常量的常指针,很容易理解,主要是如何区分常量指针和指针常量.
    • 一种方式是看 * 和 const 的排列顺序,比如
    int const* p;    //const * 即常量指针
    const int* p;    //const * 即常量指针
    int* const p;    //* const 即指针常量
    
    • 还一种方式是看const离谁近,即从右往左看,比如
    int const* p;    //const修饰的是*p,即*p的内容不可通过p改变,但p不是const,p可以修改,*p不可修改;
    const int* p;    //同上
    int* const p;    //const修饰的是p,p是指针,p指向的地址不能修改,p不能修改,但*p可以修改;
    
  • 实例
//-------常量指针-------
const int *p1 = &a;
a = 300;     //OK,仍然可以通过原来的声明修改值,
//*p1 = 56;  //Error,*p1是const int的,不可修改,即常量指针不可修改其指向地址
p1 = &b;     //OK,指针还可以指向别处,因为指针只是个变量,可以随意指向;

//-------指针常量-------//
int*  const p2 = &a;
a = 500;     //OK,仍然可以通过原来的声明修改值,
*p2 = 400;   //OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
//p2 = &b;     //Error,因为p2是const 指针,因此不能改变p2指向的内容

//-------指向常量的常量指针-------//
const int* const p3 = &a;
//*p3 = 1;    //Error
//p3 = &b;    //Error
a = 5000;    //OK,仍然可以通过原来的声明修改值
  • 在实际应用中,常量指针要比指针常量用的多,比如常量指针经常用在函数传参中,以避免函数内部修改内容。
    size_t strlen(const char* src); //常量指针,src的值不可改变;
    char a[] = "hello";
    char b[] = "world";
    size_t a1 = strlen(a);
    size_t b1 = strlen(b);
    虽然a、b是可以修改的,但是可以保证在strlen函数内部不会修改a、b的内容。
    

七.空指针与野指针

  • 空指针就是保存地址为空的指针,使用指针时必须先判断是否空指针,很多问题都是这一步导致的。
  • 野指针是在delete掉指针之后,没有置0,导致指针随意指向了一个内存地址,如果继续使用,会造成不可预知的内存错误。另外指针的误用很容易造成BUG或者内存泄漏。
//-------空指针-------//
int *p4 = NULL;
//printf("%d",*p4); //运行Error,使用指针时必须先判断是否空指针

//-------野指针(悬浮、迷途指针)-------//
int *p5 = new int(5);
delete p5;
p5 = NULL; //一定要有这一步
printf("%d",*p5);  //隐藏bug,delete掉指针后一定要置0,不然指针指向位置不可控,运行中可导致系统挂掉

//-------指针的内存泄漏-------//
int *p6 = new int(6);
p6 = new int(7); //p6原本指向的那块内存尚未释放,结果p6又指向了别处,原来new的内存无法访问,也无法delete了,造成memory leak

八.参考资料

1.菜鸟教程

2.C++中指针常量和常量指针的区别

你可能感兴趣的:(C++)