C++中const,指针和引用

2023-11-14

C++中的const,指针和引用

在线C/C++编译器,可以试着运行代码。

C++中的const

在C语言中,const修饰的量称为常变量(在编译过程中,const就是当成变量的编译生成指令的),不可以直接修改它的值,但是可以通过地址进行修改其对应的值。并且const修饰的变量可以不进行初始化,编译器最后默认赋值为0。

#include <stdio.h>
void main()
{
    const int a = 10;
    // a=30; a不能直接作为左值修改其值
    int *p = (int *)&a; // p指向a的地址,所以后面通过指针可以修改地址的值 //理论上来说是错误的,int* <- const int* 这种转化不支持,但是编译器确实通过了
    *p = 30;
    printf("%d,%d,%d", a, *p, *(&a)); // 30,30,30
    const int i;
	printf("%d",i); // 0
}

然而在C++中,这个一样的代码就会出现不一样的结果:

#include<iostream>
using namespace std;

int main()
{
    const int a = 10;
    // a=30; 不能直接作为左值进行修改其值
    int *p = (int *)&a; //理论上来说是错误的,int* <- const int* 这种转化不支持,但是编译器确实通过了
    *p = 30;
    cout << a << "," << *p << "," << *(&a) << endl; // 10,30,30 在编译过程中会直接替换a为10
    // const int i; C++中const必须初始化,也成为常量
    int array[a]; // a是常量,座椅所以可以初始化数据长度
    int b = 20;
    // int test_array[b]; b是变量,所以不能直接初始化数组长度
    return 0;
}

这是因为在C++中const修饰的变量在编译过程中会将const修饰的值直接替换为对应的值。

C++中const修饰的量称为常量必须进行初始化,不可以直接修改它的值,但是可以通过地址进行修改其对应的值

C++引用

C++引用又可以分为左值引用和右值引用

  • 左值,它有内存,有名字,值可以修改
  • 右值,没内存,没名字
  • 一个右值引用变量,其本身是一个左值

以下举例进行说明:

int i = 10; // i是一个左值,有内存,有名字,值可以修改
int &j = i; // j左值引用变量

// int m = &20; 20是一个右值,没内存,没名字
int &&m = 20;// 可以使用右值引用
m = 30;
// int &&n = m; n是一个右值引用变量,但是它本身是一个左值
int &n = m;

const int &k = 20; // int temp=20;const int &k=temp;这个代码的编译过程和int &&m=20;一样,不过m可以被修改而k不能被修改
// k = 30; k是常量,不能被修改

C++中指针和引用的区别

  1. 引用是一种更安全的指针。
  2. 引用必须初始化,指针可以不初始化。
  3. 引用只有一级引用,没有多级引用;指针可以有一级指针,也可以有多级指针。
  4. 定义一个引用变量和定义一个指针变量,其汇编指令是一模一样的;通过引用变量修改所引用的内存的值,和通过指针解引用去修改指针所指向的内存值,其底层指令是一模一样的。
int a = 0;
int b = 10;
int &c = a;
// int &d; 引用必须要初始化
int *d = &b;
int *e;                                    // 指针可以不初始化
c = 20;                                    // c就相当于a的别名,
cout << a << "," << b << "," << c << endl; // 20,10, 20
*d = 30;
cout << a << "," << b << "," << *d << endl; // 20,30, 30

函数传值和传引用的区别

#include<iostream>
using namespace std;

void swap_comm(int x,int y)
{
    int temp = x;
    x = y;
    y = temp;
}
void swap(int *x,int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
void swap(int &x,int &y)
{
    int temp = x;
    x = y;
    y = temp;
}

int main()
{
	int array[10] = {};
    int *a_p1 = array;// 指针指向数组的首地址
    int (&a_inf)[10] = array;//定义一个引用变量来存储array,也就是别名
    cout << sizeof(array) << "," << sizeof(a_p1) << "," << sizeof(a_inf) << endl; // 40,8,40

    int a = 0;
    int b = 10;

    cout << a << "," << b << endl; //0,10
    swap_comm(a, b);//传值,没啥用,会新建临时变量,更改的只是临时变量的值,不会更改a,b的值
    cout << a << "," << b << endl; // 0,10
    swap(a, b);//传引用
    cout << a << "," << b << endl; // 10,0
    swap(&a, &b);//传指针
    cout << a << "," << b << endl; // 0,10

    return 0;
}

C++中const和一二级指针的结合使用

C++中const修饰的量叫做常量,和普通变量的区别在于:1、编译的方式不同,直接替换程序中出现的名字。2、不能作为左值,也就是不能重新直接修改它对应的值,但是可以通过指针修改它的值。

const修饰的量(常量)常出现的错误是:

  • 常量不能在作为左值,直接修改常量的值。
  • 不能把常量的地址泄露给一个普通的指针或者普通的引用变量。

const和指针联用的情况,const修饰的是离它类型最近的类型:

const int *p; //可以指向其他内存的地址,但是不能用指针间接修改其对应的值,p=&a;√ *p=a;×
int const* p;
//上面两个是一样的,不过常用第一种

int *const p; //指针p是常量,不能指向其他内存,但是可以通过指针间接修改内存所指向的值 *p=a;√ p=&a;×

const int *const p;//既不可以修改p指针,也不可以通过指针修改指向的内存

const和指针类型转化

int* q1 = nullptr;
int* const q2 = nullptr; // const右边没有*,则不参与类型
// int*,int *
cout << typeid(q1).name() << "," << typeid(q2).name() << endl;

int a = 10;
int* p1 = &a;
const int* p2 = &a; // const int* <- int*
int* const p3 = &a; // int*       <- int*
int* p4 = p3;       // int*       <- int*

// const int** m = &p; // const int ** <= int **

总结const和指针的类型转化公式:

int* <= const int* 不可以的
const int* <= int* 可以的
const int** <= int** 不可以的
int** <= const int** 不可以的
int** <= int* const* 等价于 int*<=const int*  不可以的
int* const* <= int** 等价于 const int* <=int* 可以的

C++ 底层const二级指针为何能被非const指针初始化? - 张三的回答 - 知乎解释了二级指针和二级常量指针的转化问题。

int* p = nullptr;
const int** cpp = &p; //假如这行代码是正确的...,那么后面 *cpp=&ci;就会出现问题

const int ci = 0;
*cpp = &ci; // const int* *cpp=const int* ci;看上去没有问题,但是 *cpp本质上就是p,而p指向了ci的地址,就出现了int* <- const int*

const,指针,引用判断正误

以下简要说明const和二级指针的用法问题

/*
int a = 10;
const int* p = &a; // const int* <- int*
int* const* q = &p; // int* const* <- const int **;等价于int* <- const int*。错误
*/

/*
int a = 10;
int*const p = &a; // int* <- int*;
int** q = &p;//int** <- int* const*;错误,因为p取了地址,所以需要考虑const
*/

/*
int a = 10;
int* p = &a; // int* <- int*;
int** const q = &p;//int** <- int**;
*/

/*
int a = 10;
int* p = &a; // int* <- int*;
int* const* q = &p;//int* const* <- int**;
*/

/*
int a = 10;
int* p = &a; // int* <- int*;
const int** q = &p; // const int ** <- int**; 错误,不支持转化
*/

/*
int a = 10;
int* const p = &a; // int* <- int*;
const int* q = p; // const int* <- int*
*/

/*
int a = 10;
int* const p = &a; //int* <- int*; const后面没有*所以不参与类型转化
int* const q = p; //int* <- int*
*/

/*
int a = 10;
int* const p = &a; //int* <- int*; const后面没有*所以不参与类型转化
int* q = p; //int* <- int*
*/

/*
int a = 10;
const int* p = &a; //const int* <- int *;
int* q = p; //int* <- const int*;错误
*/

const和一级指针和引用结合的代码分析:

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

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

int a = 10;
int* const p = &a;
int*& q = p; //int** q=int* const* p; 错误
//或者 int**q=&p;p是const修饰的量,不能将其地址赋值给普通变量

int a = 10;
int* p = &a;
int*& q = p; //int** p=&q;
//写一句代码,在内存的0x0018ff4处写一个为4字节的整数10;
int* p = (int*)0x0018ff4;
*p = 10;

int a = 10;
int* p = &a;
int** q = &p;
int*& m = p; // int** m=&p;还原回来就是这样的

//判断是否正确
//const int*& k = p; // const int** k=&p; const int** <- int**;所以是错误的  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++中const,指针和引用 的相关文章

  • C# 中直接从 URL 获取图像尺寸

    我正在尝试使用以下代码直接从网络上获取图片的尺寸 string image http www hephaestusproject com csharp3 png byte imageData new WebClient DownloadDa
  • 显示 div 内的用户名列表

    我是 jQuery 新手 在我的项目中 我创建了一个类User其中代码如下所示 static ConcurrentDictionary
  • C# - Visual Studio 中的 System.OutOfMemoryException

    我遇到问题 当我右键单击 Visual Studio 中的主窗体并转到 视图设计器 时 出现错误 它说 引发了 System OutOfMemoryException 类型的异常 堆栈跟踪 at System Reflection Asse
  • 如何使用最小起订量模拟私有只读 IList 属性

    我试图嘲笑这个列表 private readonly IList
  • 是否有可能将 *.pdb 文件包含到发布版本中以查看错误行号?

    我做了一个项目 所有设置都是默认的 当我在调试模式 构建配置 调试 下运行它并遇到异常时 它转储到我的自定义日志记录机制 其中包含错误行号 但是当我运行发布构建时 记录相同的异常 没有行号 只有方法抛出和记录调用堆栈 是否有可能在发布配置
  • JetBrains Rider 针对 4.5 框架,无法切换到 4.7

    基本上 当尝试添加不支持旧框架的 NuGet 包时 会出现错误 但是在项目配置中只有 4 5 可用 在项目创建过程中 不存在选择目标的选项 有什么方法可以正确配置它吗 I haven t found out how to set up NE
  • 多个线程访问一个变量

    我在正在读的一本教科书中发现了这个问题 下面也给出了解决方案 我无法理解最小值怎么可能是 2 为什么一个线程不能读取 0 而所有其他线程都执行并写入 1 而无论是1还是2 最后写入的线程仍然必须完成自己的循环 int n 0 int mai
  • 根据 Active Directory 策略检查密码[重复]

    这个问题在这里已经有答案了 我有一个允许用户更改其 AD 密码的前端 有没有办法获取特定用户及其属性 长度 复杂性 的密码策略 例如细粒度 有没有办法根据此特定策略检查字符串 xyz121 编辑 我不想检查活动目录中存储的当前密码 我想检查
  • 为什么 rand() 总是返回相同的值? [复制]

    这个问题在这里已经有答案了 可能的重复 在C中生成随机数 https stackoverflow com questions 3067364 generating random numbers in c 使用 rand 生成随机数 http
  • 使用 catch all 字典属性将 json 序列化为对象

    我想使用 JSON net 反序列化为对象 但将未映射的属性放入字典属性中 是否可以 例如给定 json one 1 two 2 three 3 和 C 类 public class Mapped public int One get se
  • 何时分离或加入 boost 线程?

    我有一个方法 大约每 30 秒触发一次 我需要在一个线程中包含它 我有一个可以从类外调用的方法 像 call Threaded Method 这样的东西会创建一个线程 该线程本身会调用最终的线程方法 这些是 MyClass 的方法 void
  • 这些工作队列标志意味着什么?

    在研究工作队列时 我遇到了内核中定义的工作队列标志和常量 我有以下我无法理解的疑问 这里的排水和救援到底是什么意思 WQ DRAINING 1 lt lt 6 internal workqueue is draining WQ RESCUE
  • Code::Blocks 中的调试似乎不起作用 - 缺少调试符号

    我正在尝试在 Code Blocks 中调试程序 我跟着本指南 http wiki codeblocks org index php title Debugging with Code Blocks and 这个短视频 http www y
  • 删除数组时出现访问冲突异常

    删除分配的内存时 出现 访问冲突读取位置 异常 如下所示 我有一个针对 Visual Studio 2010 工具集 v100 C 编译器编译的本机 dll 我有一个针对它的托管 dll 包装器 它是针对工具集 v90 编译的 因为我想以
  • 文本框中“结束编辑”的事件

    我正在 winform c 中使用文本框 并使用文本在数据库中进行查询 但每次文本更改时 我都需要不断查阅文本框的文本 因此 对于这些 我使用 KeyUp 但这个活动太慢了 文本框编辑完成后是否会触发任何事件 我考虑完成2个条件 控制失去焦
  • 按 Enter 继续

    这不起作用 string temp cout lt lt Press Enter to Continue cin gt gt temp cout lt lt Press Enter to Continue cin ignore 或更好 in
  • 如何使 WinForms UserControl 填充其容器的大小

    我正在尝试创建一个多布局主屏幕应用程序 我在顶部有一些按钮链接到应用程序的主要部分 例如模型中每个实体的管理窗口 单击这些按钮中的任何一个都会在面板中显示关联的用户控件 面板包含用户控件 而用户控件又包含用户界面 WinForms User
  • C# 模式匹配

    我对 C 有点陌生 我正在寻找一个字符串匹配模式来执行以下操作 我有一个像这样的字符串 该书将在 唐宁街 11 号接待处 并将由主要医疗保健人员参加 我需要创建一个 span 标签来使用 startIndex 和 length 突出显示一些
  • 使用方法的状态模式

    我正在尝试使用方法作为状态而不是类来基于状态模式的修改版本来实现一个简单的状态机 如下所示 private Action
  • 线程安全的有限大小队列,不使用锁

    我正在尝试编写一个主题队列 但遇到死锁和其他多线程问题 我想用Interlocked CompareExchange避免lock用法 但这段代码并没有按预期工作 它只是擦除整个队列 我在这里做错了什么 public class FixedS

随机推荐

  • 8位深, 16位深,24位深,32位深图片显示原理及对比

    我们都知道一张图片可以保存为很多种不同的格式 比如bmp png jpeg gif等等 这个是从文件格式的角度看 我们抛开文件格式 看图片本身 我们可以分为8位 16位 24位 32位等 单击右键 属性 gt 详细信息即可查看图片位深度 8
  • ijkplayer-android编译 兼容多视频格式

    用的播放器框架是ijkplayer 然后就去https github com Bilibili ijkplayer的issue里找答案发现很多人遇到了这个问题 最终还是想通过编译定制化so的方式解决 践行解决方案 按照官方文档编译andro
  • Mybatis-代码走查问题整理

    实践篇 1 和 的区别 直接替换变量 有sql注入风险 使用场景 当表名 字段名作为变量传入时 PreparedStatement 预处理编译 先替换为 然后赋值 添加单引号 2 使用注解和xml文件sql的方式区别 注解 简单明了 无需额
  • Doris之Binlog Load

    Binlog Load Binlog Load提供了一种使Doris增量同步用户在Mysql数据库的对数据更新操作的CDC Change Data Capture 功能 适用场景 INSERT UPDATE DELETE支持 过滤Query
  • Qt6教程之三(3) QtWedget自定义控件

    在之前的博客中 我们使用的控件都是Qt官方提供的 对于控件的特性也只能被动地接受 为了打破这种束缚 可以按照自己的想法来定义控件 不过自定义控件必须遵守Qt官方的一套自定义控件规则 在规则之下我们就可以定义属于我们自己的控件啦 QWidge
  • 输出比较实验

    转载链接 输出比较 输出比较就是通过定时器的外部引脚对外输出控制信号 输出比较的模式有哪些 可以设置为以下几种不同的模式 翻译如下 000 冻结 输出比较寄存器TIMx CCR1中的内容与计数器TIMx CNT中的内容之间的比较对输出无影响
  • jquery ajax 无效字符,【JQuery】 ajax 无效的JSON基元

    如题 个人理解就是 你向传数据 josn格式 了 但是后台接受确不是json格式的 数据 贴段代码 var strJson usercode 123 password 123 ajax type POST url Index doLogin
  • CentOS7下安装 mysql5.7.25(glibc版)(可用)

    一 安装前的检查 1 检查 linux 系统版本 root localhost cat etc system release 2 检查是否安装了 mysql mysql 有三种安装方式 二进制包安装 RPM包安装 源码装 3 系统内存检查
  • c++的cout输出

    1 c 的cout输出顺序是从左往右进行输出 但是是从右往左压入栈 2 c 的cout的输出是遇到函数是若函数内右cout的操作则立即执行 3 不同编译器对相同语句的编译规则是不一样的 所以最好不要对同一变量进行多次的修改 也不用深究 没有
  • go语言如何编译为可执行文件

    使用系统自带的cmd找到main函数所在位置 1 go build go即可把go程序编译成exe文件 2 go run go就可以运行go程序了 3 便宜源代码 官方说使用go build fileName 编译出来的就直接带有调试信息了
  • bash: ifconfig: 未找到命令 解决方案

    解决思路 1 ifconfig 命令存在的情况 首先查看 ifconfig 命令在哪个目录下 顺便检查是否安装了这个命令 whereis ifconfig 然后查看 echo PATH PATH 中是否包含了这个目录 一般情况下是不包含的
  • 前端开发都需要掌握那些技术?

    前端技术多且杂 那么作为前端开发者 我们可以从那些方面进行进阶提升呢 本文从以下几个方面进行了整理归纳 内容如下 一 网页开发 二 小程序 三 移动端 四 桌面端 五 其他技术 一 网页开发 这里指PC端网页开发 要求的技术主要有以下几类
  • 华为OD机试(JAVA)真题 -- 最长(连续)子串

    import java util Scanner 给定一个字符串 只包含字母和数字 按要求找出字符串中的最长 连续 子串 字符串本身是其最长的子串 子串要求 只包含 1 个字母 a z A Z 其余必须是数字 字母可以在子串中的任意位置 如
  • shell自动部署docker及docker-compose

    一 准备环境 centos7 6及centos7 9已通过测试 测试时服务器刚刚完成初始化 未修改任何配置 可以直接运行脚本进行安装 要求主机可以访问互联网 yum环境在脚本中已自动准备 将两个脚本直接复制放到root目录下 赋予可执行权限
  • NGUI学习笔记汇总

    NGUI学习笔记汇总 适用于NGUI2 x NGUI3 x 一 NGUI的直接用法 1 Attach a Collider 表示为NGUI的某些物体添加碰撞器 如果界面是用NGUI做的 只能这样添加 注 用Component添加无效 2 A
  • 前端WEB安全

    一 浏览器安全 首先了解前端web安全知识 比不可绕开的基础就是同源策略了 同源策略 Same Origin Policy 是一种约定 它是浏览器最核心也最基本的安全功能 如果缺少了同源策略 则浏览器的正常功能可能都会受到影响 可以说Web
  • 你想改变现在的生活吗?

    你想改变现在的生活吗 你想加薪吗 想买车吗 想找到你生命中的另一半吗 去学习吧 学校是个神奇的地方
  • 记一次解决联想笔记本冬天卡顿反应慢的方法

    个人简介 个人主页 九黎aj 幸福源自奋斗 平凡造就不凡 如果文章对你有用 麻烦关注点赞收藏走一波 感谢支持 欢迎订阅我的专栏 autojs 图携带方便买了一个联想笔记本 然后发现笔记本充电时候正常 不卡 不充电时候会卡0 4Ghz ctr
  • Java包装类、自动装箱与拆箱知识总结

    因为在学习集合时知道集合里存放的对象都是Object类型 取出的时候需要强制类型转换为目标类型 使用泛型集合不需要 如int a Integer arrayList get 0 然后我们就会发现 为什么要强制转换为Integer 而不是in
  • C++中const,指针和引用

    C 中的const 指针和引用 在线C C 编译器 可以试着运行代码 C 中的const 在C语言中 const修饰的量称为常变量 在编译过程中 const就是当成变量的编译生成指令的 不可以直接修改它的值 但是可以通过地址进行修改其对应的