前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c和c++的区别(二)const和引用、一级指针、二级指针的结合

c和c++的区别(二)const和引用、一级指针、二级指针的结合

作者头像
lexingsen
发布2022-02-24 15:56:54
4300
发布2022-02-24 15:56:54
举报
文章被收录于专栏:乐行僧的博客乐行僧的博客

一、const和一级指针的结合

一级指针的模型

这里写图片描述
这里写图片描述

一级指针有两种表达方式,p*p。所以const与一级指针有两种结合方式。

代码语言:javascript
复制
//在c++语法规则中,const修饰距离它最近的类型。
int a=10;
int *p=&a;
int const *p;
//距离const最近的类型是int,而不是int*,因为int已经是类型了
//const的是*p,p本身没有被const修饰
const int *p;
//距离const最近的是int,*不能构成类型。const修饰的是*p,p没有被修饰
int* const p;
//距离const最近的类型是int*,修饰的是一个指针变量p。但*p没有被修改
//存在内存泄漏
const int* const p;
//距离第一个const最近的类型是int,修饰的是*p。距离第二个const的类型
//是int*,所以修饰的p。

在C++中,定义常量必须进行初始化。那么上边四个哪些是常量?

代码语言:javascript
复制
int a=10;
int *p=&a;
int const *p;//const修饰*p,但是没有修饰p。p可更改,故不是常量。
const int *p;//const修饰*p,没有修饰p。p可更改,故不是常量。
int* const p;//const修饰的是p,是常量。
const int const* p;//变量名p本身被const修饰,故是常量。
代码语言:javascript
复制
int main(){
	int a=10;
	const int b=20;
	a=b;//正确,将常量值赋值给变量
	b=a;//错误,常量不能作左值
}

在C++中,当const关键字修饰常量时,const所在的位置,会不会出现问题。主要是担心代码会修改被const修饰的常量值,如果有这样的风险,编译器不会通过代码的编译的。

修改的方式有两种: 1.直接修改 直接修改比较容易判断,看常量是否作左值。 2.间接修改 会不会将常量的引用或地址泄漏出去,通过使用引用(使用引用会自动解引用)或指针间接修改常量。

代码语言:javascript
复制
一级指针与const结合总结:
const int*  ->  int*     //错误
int*        ->  const int*    //正确

测试一:test.cpp

代码语言:javascript
复制
int main(){
	/*
	int a=10;
	int *p=&a;//&a  int*     正确的赋值
	*/
	const int b=10;
	int *p = &b;
	const int* q = &b;
	//&b -> const int* ,将常量的地址泄漏出去了
	//但是泄漏出去不一定是错误的,且看下边的例子
	
	*p=20;//错误,可以通过*p修改b内存块的地址。const没有修饰*p
	//存在间接修改常量内存块的风险,编译是不通过的
	*q=20;//此时q为const int*,不能作左值,编译错误
}
这里写图片描述
这里写图片描述

测试二:test1.cpp

代码语言:javascript
复制
int main(){
//对于const int*,可以存储常量的地址,也可以存储变量的地址
	int a=10;// const int a=10;
	const int* p=&a;
	int* q=p;//直观的感受q就是&a啊,a是变量,可以通过*q修改a
	/*但是编译是错误的,为什么呢?
	对于const int* p,其类型为const int*,不管存储的常量的地址
	还是变量的地址,都按照其类型存储,即const int*,即使是存储
	的是变量的地址也会提升为常量的地址。
	int *q=p;*q并没被const的修饰,所以会出现编译错误
	*/
	可见,其实const int*里边存储与变量a无关
	return 0;
}
这里写图片描述
这里写图片描述

测试三:test2.cpp

代码语言:javascript
复制
test2.cpp
int main(){
	const int* p=NULL;
	int* q=p;
}
这里写图片描述
这里写图片描述

测试四:test3.cpp

代码语言:javascript
复制
//输出类型
test3.cpp
#include<iostream>
#include<typeinfo>
using namespace std;
int main(){

	int a=10;
	int const *p=&a;
	int *const q=&a;
	cout<<typeid(p).name()<<endl;
	cout<<typeid(q).name()<<endl;
	return 0;
}
这里写图片描述
这里写图片描述

有上图结果可知,const没有修饰*(指针)/&(引用),不用考虑。

二、const和引用的结合 定义引用时,由于&和变量名紧挨着。所以const和引用结合只有一种方式,即const int &变量名int const &变量名,而不会出现int &const 变量名这种形式。

代码语言:javascript
复制
int main(){
	//int a=10;
	//int &b=a;
	//const int &c=a;
	
	const int a=10;
	int &b=a;//错误,将a的引用泄露出去,通过对b赋值可以修改常量
	//对于常变量只能使用常引用
	const int a=10;
	const int& b=a;
	return 0;
}

常引用 const&引用常量(包括可寻址的常量和不可寻址的常量)

代码语言:javascript
复制
int main(){
	int &a=10;//错误,不能用立即数进行初始化
	const int &b=10;//正确的,为什么呢?
	return 0;
}

从汇编的角度看看常引用为什么是可行的,往往越底层的东西越能带来透彻的理解。

这里写图片描述
这里写图片描述
代码语言:javascript
复制
const int& a=10;
mov dword ptr[ebp-14h],OAh
//函数栈帧空间以栈底指针ebp的偏移量offset表示栈空间的地址
//将OAh(10)存到[ebp-14h]指向的四字节的内存空间中
mov eax,[ebp-14h]
//将地址[ebp-14h]存放到eax寄存器中
mov dword ptr[a],eax
//将eax寄存器中的内容即[ebp-14h]存放到地址为a四字节的空间[a]中

通过上边汇编代码的分析,所谓常引用,实际上是在内存中寻取了一块空间,作为临时量,存放立即数。而引用则是对这块内存空间即临时量的引用。

代码语言:javascript
复制
const int &a=10;//可以看作是下边两行代码
const int temp=10;
const int &a=temp;

指针变量与常引用结合

代码语言:javascript
复制
如现在要向地址为0x0011ff22内存块写入10,定义指针的引用变量
int main(){
	int *&p = (int*)0x0011fff22;*p=10;
	//显然这是错误,引用不能用立即数初始化
	//结合上边的常引用
	const int*&p还是int* const &p哪一个是正确的呢?
	const int*&p其中,const修饰的是*p,并非引用,是错误的
	int * const &p;是正确的
	
	int* const &p=(int*)0x0011ff22;//可以看作是
	int* const temp=(int*)0x0011ff22;//临时量存储立即数
	int* const &p=temp;	
}

引用不参与类型,不能说是引用类型

代码语言:javascript
复制
#include<iostream>
#include<typeinfo>
using namespace std;

int main(){
	int a=10;
	int &b=a;
	cout<<typeid(b).name()<<endl;
	return 0;
}
这里写图片描述
这里写图片描述

可见引用不参与类型,但是指针是参与类型的。

三、const和二级指针的结合 二级指针的模型

这里写图片描述
这里写图片描述

二级指针有三种表达方式,即q*q**q,所以const和二级指针最基本的结合方式有三种。

代码语言:javascript
复制
int const **q;//修饰的是**q,没有修饰*q和q
int* const *q;//修饰的是*q,没有修饰**q和q
int** const q;//修饰的是q,没有修饰**q和*q

二级指针和const结合的典型问题 1.

代码语言:javascript
复制
int main(){
	int a=10;
	int* p=&a;
	const int** q=&p;
	//错误   **q和*p是等价的,*q和p是等价的
	//由于const修饰了**q,所以不需考虑通过*p修改常量的值
	//*q是const int*类型
	//const int a=10;   &a  -> const int*
	//*q=&a    由于*q和p等价      p=&a
	//所以存在通过对*q解引用修改常量内存块的风险
	//通过对p解引用修改常量内存块的风险

	以下两种修改方式均是正确的
	int a=10;
	const int *p=&a;
	const int **q=&p
	或
	int a=10;
	int *p=&a;
	const int* const *q=&p;	
}

2.

代码语言:javascript
复制
int main(){
	int a=10;
	int *p=&a;
	const int *&q=p;//   q和p等价,错误同上
	const int **q=&p;
}

3.

代码语言:javascript
复制
int main(){
	int a=10;
	int *p=&a;
	int* const *q=&p;//正确
	
	int a=10;
	int *p=&a;
	int** const q=&p;//正确
}

4.

代码语言:javascript
复制
int main(){
	int a=10;
	const int* p=&a;//const修饰的是*p即内存a被封锁
	int** q=&p;//错误,通过**q可以将常量内存块修改
	改正为:int const **q=&p;
}

5.

代码语言:javascript
复制
int main(){
	int a=10;
	int* const p=&a;//const修饰的是p
	int** q=&p;//错误,通过*q可以修改常量内存块的值
	改正为:int* const *q=&p;
}

综上:当一级指针、二级指针和const结合时。 1.`const int*` 转化为`int* ` 错误 2.`int*` 转化为`const int*`正确 3.`const int** `转化为`int**`错误 4.`int** `转化为`const int**`错误 5.当const为`**`之间时,`*const*退化为一级指针考虑。`

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档