先初步了解引用、指针的一些注意事项。
- 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型指针
有例外:引入const 限定符
初始化常量引用时允许用任意表达式作为初始值
- 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; //错误
定义:又叫常指针,可以理解为常量的指针,也即这个是指针,但指向的是个常量,这个常量是指针的值(地址),而不是地址指向的值。
关键点:
代码形式:
- int const* p; const int* p;
定义:本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
关键点:
代码形式:
- int* const p;
定义:指向常量的指针常量就是一个常量,且它指向的对象也是一个常量。
关键点:
代码形式:
- 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