【c++】const的使用(包括与引用、指针、形参的结合等)

目录

 

一、const在c语言和c++中的区别      

二、const修饰的变量称为常量,具有以下的特点:

三、const与指针、引用的结合

(一)const与指针的结合

(二)const与引用的结合:

四、const、引用与形参的结合

五、引用、指针与返回值的结合以及接受返回值时的使用


一、const在c语言和c++中的区别      

        在c语言的学习中,我们都接触过const的使用规则,在c语言中,const所修饰的变量称为常变量,而常变量的处理是在编译阶段判断长变量又没有做左值,其他与普通变量处理相同。详情请见const规则详解(c语言版)。
        而在c++中,const所修饰的变量称为常量,在编译阶段将用到的常量替换成常量初始化的值。

二、const修饰的变量称为常量,具有以下的特点:

  1. 一定要初始化(因为在编译阶段要进行替换);
  2. 不能有修改常量内存块的风险;
  3. const修饰的全局变量的符号是一个local的属性;
  4. 用变量来初始化常量,常量退化为常变量;
代码一:(1、2)
const int a = 10;
const int *p = &a;//错误,即该句为*p = &a;
//a在编译阶段会被替换为10,10会被存在寄存器中,对它不能进行取地址
*p = 30;//错误

代码二:(3)
“test.cpp”
extern const int gdata = 10;//存放在.data段,是一个global属性

“main.cpp”
extern const int gdata;//"UND"  声明一个外部符号
int main()
{
    const int a = gdata;//引用错误
}

代码四:(4)
int a = 10;
int arr[a] ;//正确  在编译阶段a被替换为数字10

int b = 10;
const int c = b;//a退化为常变量
int arr[c];//错误  常变量不能用来给出数组的下标

那么,在了解到const在c++中的使用规则之后,接下来,进一步了解一下const与指针、引用的结合的使用规则。

三、const与指针、引用的结合

(一)const与指针的结合

如果const位于*的左边,则const就是用来修饰指针所指向的变量,即指针指向为常量;

如果const位于*的右边,const就是修饰指针本身,即指针本身是常量。

const int a = 10;
const int *p = &a;//定义指向常量的指针p,可以改变指针的指向,但是不能通过指针来改变 x中的值
int * const p2= &a;//定义常指针p2,不可改变指针的指向(常指针),但可以通过指针来修改x中的值
const int * const p3=&x;//不可以修改指针的指向,也不能修改通过指针修改变量的值

1、一级指针
    无const时的指针用法
    int a = 10;
    int *p = &a;
    int *q = p;

    这三句代码就是我们平常用到的指针的简单使用,p存放的是内存单元a的地址,而将q赋值为p,那么q也存放的是a的地址,p,q都会间接访问到内存块a,进而会对其值进行修改。


           有const修饰时的使用:有const修饰:杜绝间接访问修改常量内存块的风险
    (1)
    const int a = 10;
    int *p = &a;//错误
    int *q = p;//错误

分析:首先由于变量a被const所修饰,其为一个常量。那么,根据const修饰的变量的特点可知,不能出现有修改常量内存块的风险。但是,在这里指针p可间接访问到常量内存块a,那么在第三句中,q也会间接访问到常量内存块,也就是说a存在被修改的风险。我们可以利用const修饰间接访问符,进而杜绝间接访问的可能,修改成以下就正确。

    const int a = 10;
    const int *p = &a;
    const int *q = p;
    (2)
    int a = 10;
    const int *p = &a;
    int *q = p;//错误

分析:这里的直接访问为a,间接访问有*p 和*q,p的指向为常量,但是*p由于有const的修饰,以杜绝访问常量内存块修改的风险。然而,仍然可通过*q的指向来间接访问内存块修改,所以第三句代码是错的。可以通过给第三句加上const修饰来杜绝风险。修改如下:

    int a = 10;
    const int *p = &a;
    const int *q = p;
    (3)
    int a = 10;
    int * const p = &a;
    int *q = p;

分析:const修饰的是p,那么p为一个常指针,直接访问a的有a,p。这里不存在间接访问。这几行代码是正确的。

  (4)
    int a = 10;
    int *p = &a;
    const int *q = p;

分析:这里的直接访问为a,*p和*q是间接访问,因为a是一个普通变量,可以通过*p去访问并修改其值。const修饰的是数据类型int*,因此这里的q指向的是一个常量内存块,存在风险但又有const修饰而被杜绝掉。因此这几行代码也是正确的。

(5)
    int a = 10;
    int *p = &a;
    int const *q = p;

分析:前两行代码是指针的普通使用,在第三行中,q被const修饰,因此q是一个常指针,指向a,它没有被间接访问,不存在风险。这几行代码是正确的。

2.与二级指针的结合

无const修饰的二级指针的使用
    int a = 10;
    int *p = &a;
    int **q = p;

这几句代码是二级指针的普通使用,p指向内存块a,q指向指针p的内存块通过p之间接修改a的值,通过q可间接修改p的指向和a的值。

2.const和二级指针的结合
  
 (1)
    const int a = 10;
    int *P = &a;//错误
    int **q = &p;    //错误

分析:const修饰a,则a是常量内存块。那么,*p可以间接访问常量内存块a,因此存在修改的风险;而**q也可以间接访问到常量内存块a。那么,可以利用const来修饰,杜绝这样的风险。修改后的如下:

    const int a = 10;
    const  int *P = &a;
    const  int **q = &p;  
    (2)
    int a = 10;
    const int *p = &a;
    int **q = &p;

分析:const修饰类型int*,那么p的指向为常量。**q可间接访问到p的指向也就是常量内存块。因此,第三句存在错误。修改如下:

    int a = 10;
    const int *p = &a;
    const  int **q = &p;
    (3)
    int a = 10;
    int *const p = &a;
    int **q = &p;

分析:const直接修饰p,那么p是一个常指针,也就是说不能间接来修改p的指向。但是在第三句中,q为二级指针,对q进行一次解引用后会访问到p的内存地址,存在对p的指向进行修改的风险。修改如下:

    int a = 10;
    int *const p = &a;
    int *const *q = &p;
    (4)
    int a = 10;
    int *p = &a;
    const int **q = &q;

分析:const修饰类型int**,那么就是说q的指向为常量,q的类型为const int **,而&p的类型为int **,在c++中,是不允许将int**转换为const int **。因为它之间存在修改的常量内存的风险。修改后的正确表示如下:

    int a = 10;
    int *p = &a;
    const int *const *q = &q;

或者

    int a = 10;
    const  int *p = &a;
    const int **q = &q;

对于int**不能转换为const  int **,我们看这几行代码:

    const int a = 10;
    int *P;
    const int **q = &p;
    *p = &a;//错误
    **q = 20;

*q存在修改常量内存块的风险。
    (5)
    int a = 10;
    int *p = &a;
    int *const *q = &q;

分析:const修饰的是*q,直接访问是p,间接访问是*q,并且*q被const修饰,则不会存在修改的风险。该代码正确。

(6)
    int a = 10;
    int *p = &a;
    int **const q = &q;

分析:const修饰q,直接访问为q,无间接访问。则无修改常量内存块的风险,该代码正确。

(二)const与引用的结合:

1、引用

(1)引用传入

  •     表层:引用时内存块的别名

          int a = 10;
          int &b = a;

  •     底层:c++中和指针的处理方式相同,即在用到引用变量的地方,系统会自动进行解引用。

         上述代码就相当于:int a = 10;
                                              int *b = &a;
(2)特点

  •     引用必须初始化

    int a = 10;
    int &b ;//错误
    int &b = a;//正确

  •     引用初始化的变量一定要可以取地址

    int a = 10;
    int &b = 10;//错误 
    像10这样的立即数是存放在寄存器当中的,是不能对立即数取地址的;

  •     引用是不可改变的

    int a = 10;
    int c = 20;
    int &b = a;
    &b = c;//错误

  •      引用只能访问引用变量所引用的内存单元

    int a = 10;
    int c = 20;
    int &b = a;
    cout << b << endl;//10 
    cout << &b << endl;//&a
2、const与引用、与引用和指针的结合使用

 (1)
   int a = 10;
   int &b = a;
   int &c = 20;//错误

分析:这三行是对引用的一般使用,根据引用的特点是它的初始化的变量必须可以取地址,但20这样的立即数不可以。因此,可以利用const修饰,形成常引用,常引用是可以引用立即数的,此时将立即数存放到临时变量中,常引用来引用临时变量。正确的写法如下:

 const int &c = 20;//引用的是临时量
(2)
   const int a = 10;
    int &b = a;//错误

分析:a是常量内存块,b和a的内存保持一致,存在修改的风险,加const杜绝。修改:const int & b = a;

(3)
   int a = 10;
   const int &b = a;//const int   <== int

判断条件:左操作数的权限 <= 右操作数的权限  

除去一种特殊情况即左const int *;对应的右为int *这是是错误的。
(4)
   int a = 10;
   int *p = &a;
   int *&q = p;

分析:q是p的别名,p,q均为一级指针,左右操作权限相等,正确。

(5)
   int a = 10;
   const int *p = &a;
   int *&q = p;//int * <= const int *错误

分析:第三句左操作数的权限大于右操作数的权限,错误。修改:

const int *&q = p;
(6)
   int a = 10;
   int *const p = &a;
   int *&q = p;//左:int *,右:int * const 错误

分析:左操作数的权限大于右操作数的权限,错误。修改:

int *const &q= p;
(7)
   int a = 10;
   int * p = &a;
   const int *&q = p;//左:const int * ;右:int *,特殊情况,错误

分析:隐藏的将int **转换为了const int **,这是错误的,有修改常量内存块的风险。修改:

   int a = 10;
   int * p = &a;
   const int * const  &q = p;

或:

   int a = 10;
   const  int * p = &a;
   const int * &q = p;
(8)
   int a = 10;
   int * p = &a;
   int *const&q = p;//左:int *const ;右:int *

分析:左操作数的权限小于右操作数的权限,正确。

四、const、引用与形参的结合

形参加上const:

  1. 防止实参被修改;
  2. 可以用来引用立即数;

引用做形参和普通变量做形参的区别:

  1.     引用修改实参的值;
  2.     引用不能引用立即数,部分实参无法调用;

五、引用、指针与返回值的结合以及接受返回值时的使用

        在这里我们应该明确一点,函数不能返回局部变量的地址或引用;

我们从以下三个例子来分别进行研究:

(1)
	int getValue()
	{
		int tmp = 20;
		return tmp;//有寄存器eax带回,所带回的值为tmp的值20
	}
	int main()
	{
		//const int& a = getValue();//int& a = eax;
		//int b = getValue();  //int a = eax;
		//int* p = &getValue();// int* p = &eax;错误表达
		
		return 0;
	}
	(2)
	int* getValue()
	{
		static int tmp = 20;
		return tmp;//有寄存器eax带回,所带回的值为tmp的地址
	}
	int main()
	{
		int a = *getValue();//int a = *eax;即int a = &tmp;
		int* p = getValue();//int *p = eax;
		int&b = *getValue();//int &b = *eax;即int &b = tmp;
		int*& pr = getValue();//int *&pr = eax;错误;可修改为:int*const& pr = getValue();
		return 0;
	}
	(3)
	int& getValue()
	{
		static int tmp = 20;
		return tmp;	//由寄存器带回,带回的值为tmp
	}
	int main()
	{
		int a = getValue();//int a = tmp;
		int&b = getValue();//int &b= tmp;
		int* p = &getValue();//int *p = &tmp;
		return 0;
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值