指针和const

来源:互联网 发布:安庆网络广播电视 编辑:程序博客网 时间:2024/05/20 08:43

一.定义

       一般,说到指针和const,就分两种:const指针和指向const的指针。const指针是指针变量的值一经初始化,就不可以改变指向,初始化是必要的;而指向const的指针是说指针指向的变量是const的(其实这种说法也是不太准确的,和const引用有点像)可以这样理解:一种指的是你不能修改指针本身的内容,另一种指的是你不能修改指针指向的内容(只是说不能通过指针的方式去修改,如果变量本身不是const的,其实是可以通过其他的方式去修改)。

       声明指针时,可以在类型前或后使用关键字const,也可在两个位置都使用。例如,下面都是合法的声明,但是含义大不同:

const int * pOne;    //指向整形常量 的指针,它指向的值不能修改int * const pTwo;    //指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。 const int *const pThree;  //指向整形常量 的常量指针 。它既不能再指向别的常量,指向的值也不能修改。

       理解这些声明的技巧在于,查看关键字const右边来确定什么被声明为常量 ,如果该关键字的右边是类型,则值是常量;如果关键字的右边是指针变量,则指针本身是常量。下面的代码有助于说明这一点:

const int *p1;  //the int pointed to is constant //即从离变量名最近的开始解读,首先‘*’代表p1首先是个指针,然后这个指针指向的变量是const的。int * const p2; // p2 is constant, it can't point to anything else//首先,‘const’代表p2首先是个常量,然后这是个“int *”类型的常量,即常量指针。

       还有个技巧,通过上面的观察我们不难总结出一点规律,是什么呢?这个规律就是: 指向const的指针(指针指向的内容不能被修改)const关键字总是出现在的左边而const指针(指针本身不能被修改)const关健字总是出现在的右边,那不用说两个const中间加个肯定是指针本身和它指向的内容都是不能被改变的,不过还是像上面一样理解性的记忆比较好。

二.应用

1、指向const的指针:指针指向的对象是常量,那么这个对象不能被更改(也被称为底层const)。在C/C++中,常量指针是这样声明的:

const int *p;int const *p;

       使用要注意,指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改,也就是说指向const的指针可以被赋值为变量的地址,之所以叫做指向const的指针,意思是说限制了通过这个指针修改变量的值。如下面的代码:

char a[20] = "abcde1234";const char *d = a;d[0] = 'x';   //错误 不能给常量赋值a[0] = 'x';   //正确const *e=a;     //正确const *f=d;     //错误 指向const的指针不能赋值给指向非const的指针,会出现const int *不能转换到int *的问题

       感觉上面的代码相当奇怪,a指针指向的对象明明就不是常量,也能够更改,但是却不能通过d去更改,编译器给我的理由竟是不能给常量赋值,C++ primer上有这样一句话:

所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉地不去改变所指对象的值。

       
2、 const指针的值是常量(也被称为顶层const),所以不能被赋值,只能被初始化。在C/C++中,指针常量这样声明:

int a;int *const b = &a; //const放在指针声明操作符的右侧

       虽然const指针的值不能变,可是它指向的对象是可变的,因为我们并没有限制它指向的对象是常量。因此,有这么段程序:

char a[20] = "abcde1234";char *const c = a;a[0] = 'c';//或者c[0] = 'c';

但是我之前看过一个sb博客上这么写的:

char *a = "abcde1234"; char *b = "bcde";char *const c = a;//下面的操作是可以的。a[0] = 'x'; // 我们并没有限制a为常量指针(指向常量的指针)//或者c[0] = 'x' // 与上面的操作一致//编译会出错,错误原因:a指针指向的是常量区,常量区具有只读属性,不可对其进行写操作

所以分析如下, “abcde1234”实质上是字符串常量,而常量往往被编译器放在只读的内存区,不可写。p初始指向这个只读的内存区,而a[0] = ‘x’则企图去写这个地方,编译器当然不会答应。
       

3、当与typedef配合使用的时候,情况又会改变。例如:下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?

typedef char * pStr;    //不能理解为简单的替换!!char string[4] = "bbc";const char *p1 =" string"; //1式const pStr p2 =" string"; //2式p1++;p2++;

问题出在p2++上,当然p1是可变的,因此问题中p1++是对的(*p1++也是对的,但是(*p1)++是错的)。替换基本形式中的type为2式中的pStr,替换后const pStr p2(但是此时不能简单的替换,不能说等价于const char * p2);此时题中的pStr就是一种新类型(就像是int一样)所以说p2相当于是指向常量的指针,所以p2++是错误的。
       

4、关于底层const和顶层const
(1)区分顶层const和底层const
指针如果添加const修饰符时有两种情况:
① 指向常量的指针:代表不能改变其指向内容的指针。声明时const可以放在类型名前后都可,拿int类型来说,声明时:const int和int const 是等价的。声明指向常量的指针也就是底层const,下面举一个例子:

int num_a = 1;  int const  *p_a = &num_a; //底层const  //*p_a = 2;  //错误,指向“常量”的指针不能改变所指的对象  

       注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。上例中指针p_a指向的内容就不是常量,可以通过赋值语句:num_a=2; 来改变它所指向的内容。

②常量指针:代表指针本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变。声明时const必须放在指针符号*后面,即:*const 。声明常量指针就是顶层const,下面举一个例子:

int num_b = 2;  int *const p_b = &num_b; //顶层const  //p_b = &num_a;  //错误,常量指针不能改变存储的地址值

其实顶层const和底层const很简单,一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。

(2)顶层const和底层const的作用
区分顶层const和底层const,根据C++primer的解释,区分后有两个作用。
①执行对象拷贝时有限制,常量的底层const不能赋值给非常量的底层const。也就是说,你只要能正确区分顶层const和底层const,你就能避免这样的赋值错误。下面举一个例子:

int num_c = 3;  const int *p_c = &num_c;  //p_c为底层const的指针  //int *p_d = p_c;  //错误,不能将底层const指针赋值给非底层const指针  const int *p_d = p_c; //正确,可以将底层const指针复制给底层const指针

②使用命名的强制类型转换函数const_cast时,需要能够分辨底层const和顶层const,因为const_cast只能改变运算对象的底层const。下面举一个例子:

int num_e = 4;  const int &r=num_e;  //用于声明引用的const都是底层constconst int *p_e = &num_e;  //*p_e = 5;  //错误,不能改变底层const指针指向的内容  int *p_f = const_cast<int *>(p_e);  //正确,const_cast可以改变运算对象的     底层const。但是使用时一定要知道num_e不是const的类型。  *p_f = 5;  //正确,非顶层const指针可以改变指向的内容  cout << num_e;  //输出5
原创粉丝点击