IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】const、指针和引用的关系 -> 正文阅读

[C++知识库]【C++】const、指针和引用的关系

1、const 和 指针 的关系

1.1 基本情况

const 指针的关系主要存存在三种:

1、常指针(const pointer)

int* const p;

2、指针类型的常量(const to pointer)

const int *p;
int const *p;

3、指向常量的常指针

const int * const p;

那么这三种不同的类型是如何使用的呢?首先我们来看下面的代码:

#include<iostream>
using namespace std;

//1、const 和 指针 的关系

int main()
{
    int a = 10;
    int b = 20;
    int *p1 = &a;
      
    //1、const pointer
    int * const p2 = &a;
    
    //2、const to pointer
    //这两种方式等价
    const int * p3 = &a;
    //int const * p3 = &a;

    //3、
    const int * const p4 = &a;
    
    return 0;
}

1、常指针的修改

int main()
{
    cout << *p2 << endl;		//10;
    *p2 = b;					//rigth
    cout << *p2 << endl;		//20
    p2 = &b;					//error
}

我们可以发现,在const 修饰指针本身的时候(int *const p2)。我们可以修改指针解引用后的值,但是不能修改指针本身所指向的地址。

2、指针类型的常量的修改

int main()
{
    cout << *p3 << endl;
    //*p3 = b;  			//error
    cout << *p3 << endl;
    p3 = &b;				//right
    cout << *p3 << endl;
}

当指针是一个指向常量的指针时(const int *p3)。我们可以改变指针所指向的对象,但是不能修改指针解引用后的值。

3、指向常量的常指针修改

*p4 = b;	//error
p4 = &b;	//error

当指针是指向常量的常指针的时候,我们既不能修改指针本身,又不能修改指针解引用后的值。

1.2 常指针或者指针类型的常量

1.2.1 常指针 const int *p = &a;

int main()
{
    int a = 10;
    int b = 20;
    const int *p = &a;

    //int *s0 = p;            //error,可以通过s0修改p指向的内容的值。
    const int *s1 = p;        //right,类型匹配
    //int * const s2 = p;       //error,s2的指向的地址不能改变,但是可以解引用来改变p的值,错误。
    const int *const s3 = p;  //right

    return 0;
}

1.2.2 指针类型的常量 int * const p = &a;

int main()
{
    int a = 10;
    int b = 20;
    int * const p = &a;

    int *s0 = p;                //right
    const int *s1 = p;          //right
    int * const s2 = p;         //right
    const int *const s3 = p;    //right 

    system("Pause");
    return 0;
}

int *const p = &a; 这条代码表示p是一个常指针,这个指针所指向的是地址不可改变的。这个常指针存放的是a的地址。其他的指针指向的也是a的地址,这些指针的赋值是正确的。因为常指针仅仅只是规定指针p不可以指向其他的地址,并没有规定其他指针不能改变这个指针解引用的值。况且其他指针的修改也仅仅只是修改这些指针本身(s0、s1、s2、s3)解引用的值(p的值)或者指向的地址,编译可以正常通过。

1.3 const_cast

首先来看一个概念:只读变量(常变量)。

const int n = 5;
int a[n];

//C99:error
//C++:right

//ANSI C规定数组的大小必须是一个常量,常量不等于“不可变的变量”,
//但是C++中没有规定。

//常量的定义:enum枚举类型和#define宏定义定义的。
//只读变量:const 来定义。

//原因:常量被编译器存放在内存中的只读区域,不可修改。
//	   只读变量会在内存中申请一块空间来存放它的值,只不过是编译器限定的不可修改。

所以说,在C++中我们会将常变量看作为常量。

int main()
{
    const int a = 10;
    int b = 20;
    int *p = &a;		//error:这样改变解引用的值会修改原先a的值,但是a是						  //const类型不可修改,报错。
    *p = b;
    
    //int *p = const_cast<int *>(&a);
}

这段代码编译器会报错,原因如下:

在这里插入图片描述

要解决可以使用 C++的标准转换运算符const_cast。

C++提供了四个转换运算符来移除变量的const限定符:

  • const_cast (expression)
  • static_cast <new_type> (expression)
  • reinterpret_cast <new_type> (expression)
  • dynamic_cast <new_type> (expression)

只需将代码改为 int *p = const_cast<int *>(&a); 即可移除变量a的const 限定符,实现初始化。

2、const 和引用 的关系

2.1 什么是引用

引用就是某个变量的别名,对引用的操作对与变量的直接操作完全相同。

格式为:类型 & 引用变量名 = 已定义过的变量名。

2.2 引用和指针的区别

引用指针
必须初始化可以不初始化
不可为空可以为空
不能更换目标可以更换目标
没有二级引用存在二级指针

1、引用必须初始化,而指针可以不初始化

int &s;		//error:引用没用初始化
int *p;		//right:指针不强制初始化

2、引用不可以为空,指针可以为空

int &s = NULL;		//error:引用不可以为空,右值必须是已经定义过的变量名
int *p = NULL;		//right:可以定义空指针。

int fun_p(int *p)
{
    if(p != NULL)		//因为指针可以为空,所以在输出前需要判断。
    {
        cout << *p << endl;
    }
    return *p;
}

int fun_s(int &s)
{
    cout << s << endl;		//引用不为空,可以直接输出
    
    return s;
}

3、引用不能改变目标,指针可以更换目标

int main()
{
    int a = 20;
    int b = 10;

    int &s = a;
    int *p = &a;

    s = b;                  //引用只能指向初始化时的对象,如果改变,原先对象也跟着改变。
    p = &b;                 //指针可以改变指向的对象,不改变原先对象。

    cout << s << endl;      
    cout << a << endl;

    system("pause");
    return 0;
}

2.3 指针和引用性能差异

指针:

void fun_p(int *p)
{
    *p = 10;
    return;
}
int main()
{
    int a = 20;
    
    int *p = &a;
    fun_p(p);
}

通过objdump -d 命令来查看指针的反汇编代码:

指针的反汇编代码

引用:

void fun_s(int &s)
{
    s = 10;
    return;
}

int main()
{
    int a = 20;

    int &s = a;
    fun_s(s);
}

通过objdump -d 命令来查看指针的反汇编代码:

引用的反汇编

我们可以清除的看出指针和引用的反汇编代码时基本一致的。这说明C++编译器在编译程序的时候将指针和引用编译成了完全一样的机器码。所以C++中的引用只是C++对指针操作的一个“语法糖”,在底层实现时C++编译器实现这两种操作的方法完全相同。

2.4 指针的引用

2.4.1 int *p = &a;

int main()
{
    int a = 10;
    int b = 20;
    int *p = &a;

    int *s = p;
	
//1、
    //int *&pref1 = p;          //指针的引用,p的别名叫做pref1。
    
//2、
    //int &*pref2 = p;          //error,引用不具有地址。

//3、
    const int *&pref3 = p;      //p的另一个名字叫pref3。
                                //由于是对指针解引用后的值添加const ,
                                //所以p(pref3)无法修改解引用的值
                                //但是可以改变p(pref3)所指向内容的地址
    //pref3 = b;                //error;
    p = &b;                     //right
    //pref3 = &b;

//4、
    int *const &pref4 = p;      //首先p的别名是pref4,const 修饰的是指针本身。表示指针p(pref4)无法对指针的指向进行修改。
                                //但是可以修改指针p(pref4)解引用后的值。
    //p = &b;                   //error
    *p = b;                     //right

//5、
    const int *const &pref5 = p;    //首先pref5是指针p的别名,前面的const修饰的是解引用的值,后面const修饰的是指针本身。
                                    //所以它们解引用后的值和指针本身的地址都不可修改

    system("pause");
    return 0;
}

2.4.2 const int *p = &a;

int main()
{
    int a = 20;
    int b = 10;
    
    const int *p = &a;			//const修饰的是解引用后的值,表示p解引用后的值不可修改,但是可以修改P指向的地址。
    
    int* &pref1 = p;			//error :pref1是P的别名,但是如果这样命名,我们就可以通过*pref1来修改a的值,与我们定义的const 相违背。
    *pref1 = b;
    
    
    const int* &pref2 = p;		//right:pref2是p的别名,通过*pref2也不可以修改p的值,从而改变a的值。但是我们可以通过改变p指向的地址,来指向不同的地址。这和const int *p的理念一致,所以是正确的 
    
    
    int* const &pref3 = p;		//error:pref3是P的别名,const修饰了pref3不可指向其他的地址,但是可以通过*pref3来修改*p产生的值。
    
    
    const int* const &pref4 = p;	//pref4是p4的别名,两个const 限定了它的解引用和修改指针指向的地址。并且遵循能力收缩的原则。
    
    return 0;
}

未完… …

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-11 23:48:36  更:2022-01-11 23:49:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:51:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码