当前位置: 首页 > article >正文

C++ const 修饰符深入浅出详解

C++ const 修饰符深入浅出详解
📅 更新时间:2025年6月6日
🏷️ 标签:C++ | const关键字 | 常量 | 多文件编程 | 现代C++

文章目录

  • 前言
  • 🌟 一、const 是什么?为什么要用?
    • 示例
    • ✅ const 的四大好处
  • 🧨二、const 在变量声明中的位置
    • const变量必须初始化
  • 📚三、编译器如何处理 const 修饰的变量
    • 注意
    • 技巧
  • 🚀四、const的引用
    • 介绍
    • 思考1
    • 思考2
  • 🎯五、指针和const
    • 指向常量的指针(pointer to const)
    • 指针常量vs常量指针
      • 指针常量
      • 常量指针
  • 🚀 六、const 与 constexpr:常量表达式的进阶
    • 🛠 示例代码
  • 🧩总结


前言

✨ 本文将以通俗易懂的方式,循序渐进地讲解 C++ 中 const 修饰符的多种用法,涵盖普通变量、指针、函数参数、成员函数、返回值以及与 constexpr 的结合。通过清晰的示例代码、常见误区分析和最佳实践建议,带你从基础到进阶,彻底掌握 const 的核心原理与应用场景,助你编写更安全、更高效的 C++ 代码


提示:以下是本篇文章正文内容,下面案例可供参考

🌟 一、const 是什么?为什么要用?

const 是 C++ 中的核心关键字,用于声明不可修改的变量或函数契约。它的核心作用是限制修改,从而提升代码的安全性、可读性和可维护性

示例

const 修饰普通变量是最简单的用法,表示变量值不可修改,且必须初始化

const int x = 10;
x = 20; // ❌ 编译错误,x 是只读的
#include <iostream>
const int maxSize = 100;
int main() {// maxSize = 200; // ❌ 错误,不能修改std::cout << "Max Size: " << maxSize << std::endl;return 0;
}

✅ const 的四大好处

防止误修改:避免无意中改变变量值,减少 bug。

增强函数语义:清晰表达函数或参数的只读意图。

编译期检查:让编译器帮你捕捉非法修改行为。

优化性能:编译器可对 const 变量进行优化(如存储在只读内存)

🧨二、const 在变量声明中的位置

const 关键字通常放在变量类型之前,例如:

const int a=10;

也可以放在类型之后,但这种用法较少见:

int const a=10;

可以用一个变量初始化常量, 也可以将一个常量赋值给一个变量

//可以用一个变量初始化常量
int i1 = 10;
const int i2 = i1;// 可以使用一个非 const 的变量(例如 i1)来初始化一个 const 变量(例如 i2)//也可以将一个常量赋值给一个变量
int i3 = i2;//i2是一个const的变量 可以赋给一个 非const的变量

const变量必须初始化

//错误用法,const变量必须初始化
//const int i4;

这个时候我们会有一个想法,在头文件中如果使用了cosnt,那说明必须要对变量初始化,但头文件中不是只能声明变量吗????两者会不会引发什么问题

我们接着往下看

📚三、编译器如何处理 const 修饰的变量

const修饰的变量在编译时会被视为只读,尝试修改其值会导致编译错误。此外,编译器可能会对 const 变量进行优化,如将其存储在只读内存区域

注意

默认状态下,const对象仅在文件内有效
当以编译时初始化的方式定义一个const对象时,就如对bufSize的定义一样:

const int bufSize = 512;

编译器将在编译过程中把用到该变量的地方都替换成对应的值。也就是说,编译器会找到代码中所有用到bufSize的地方,然后用512替换。

为了执行上述替换,编译器必须知道变量的初始值。

如果程序包含多个文件,则每个用了const对象的文件都必须得能访问到它的初始值才行。要做到这一点,就必须在每一个用到变量的文件中都有对它的定义.

为了支持这一用法,同时避免对同一变量的重复定义默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。

我们创建一个global.h文件和global.cpp文件, 我们知道头文件只做变量的声明,之前我们在头文件添加变量的定义会导致连接错误。
那如果我们添加const变量的定义

//global.h
const int bufSize = 100;

main.cppglobal.cpp中包含global.h,发现可以编译通过,
这是因为虽然main.cpp和global.cpp中包含了同名的bufSize,但却是不同的变量,运行程序可以编译通过

我们来跑一下代码来测试一下是否为不同变量

//global.h
#pragma once
#include<iostream>
const int a=10;void play();//global.cpp
#include<iostream>
#include"global.h"
using namespace std;void play()
{cout <<"global.cpp中 a 地址:" << &a << endl;
}//main.cpp
#include<iostream>
#include"global.h"
using namespace std;
int main()
{cout <<"main.cpp中 a的地址:" << &a << endl;play();return 0;
}输出:
main.cpp中  a的地址:00007FF629C4AC54
global.cpp中 a 地址:00007FF629C4ABB0

我们会发现这是两个不同的变量,所以链接不会爆错

那我们如果就想要同一个变量,咋办????
前置知识点:extern
可以看我前面讲解c++中的extern关键字的文章

如果我们想让所有文件共享一个变量,那我们需要这样写

技巧

如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字

//global.h
#pragma once
#include<iostream>
extern const int a;//只声明  
void play();//global.cpp
#include<iostream>
#include"global.h"
using namespace std;const int a = 10;void play()
{cout <<"global.cpp中 a 地址:" << &a << endl;
}//main.cpp
#include<iostream>
#include"global.h"
using namespace std;
int main()
{cout <<"main.cpp中 a的地址:" << &a << endl;play();return 0;
}输出:
main.cpp中  a的地址:00007FF6644EABB0
global.cpp中 a 地址:00007FF6644EABB0

很容易发现,这是同一个变量

🚀四、const的引用

介绍

可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用(reference to const)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:

//定义常量
const int ci = 1024;
//用常量引用绑定常量
const int &r1 = ci;

不能修改常量引用的值

//不能修改常量引用的值
//r1 = 2048;

也不能用非常量引用指向一个常量对象

//也不能用非常量引用指向一个常量对象
//int& r2 = ci;

术语
常量引用是对const的引用

企业中,C++程序员们经常把词组“对const的引用”简称为“常量引用

允许将const引用绑定一个非const变量

int i5 = 1024;
//允许将const int& 绑定到一个普通的int对象上
const int &r5 = i5;

常量引用绑定字面量

//常量引用绑定字面量
const int &r6 = 1024;

常量引用绑定表达式计算的值

//常量引用绑定表达式计算的值
const int &r7 = r6 * 2;
const int &r8 = i5 * 2 + 10;

思考1

下面的代码能编译通过吗?

double dval = 3.14;
int & rd = dval;

答案

//错误用法,类型不匹配
double dval = 3.14;
int & rd = dval;

思考2

下面的代码能编译通过吗?

double dval = 3.14;
const int & ri = dval;

答案

//编译通过
double dval = 3.14;
const int & ri = dval;

上面的代码相当于

//上面代码会做隐士转换,相当于下面代码
const int temp  = dval;
const int &ri = temp;

在这种情况下,ri绑定了一个临时量(temporary)对象。

所谓临时量对象就是当编译器需要一个空间来暂存表达式的求值结果时临时创建的一个未命名的对象

C++程序员们常常把临时量对象简称为临时量。

对const的引用可能引用一个并非const的对象必须认识到,常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:

int i9 = 1024;
//非常量引用绑定i9
int &r9 = i9;
//常量引用绑定一个变量
const int &r10 = i9;
//可以同过非常量引用修改i9的值
r9 = 2048;

🎯五、指针和const

指向常量的指针(pointer to const)

可以令指针指向常量非常量类似于常量引用指向常量的指针(pointer to const)不能用于改变其所指对象的值。

要想存放常量对象的地址,只能使用指向常量的指针

/PI 是一个常量,它的值不能改变
const double PI = 3.14;double * ptr = &PI;//错误!!!,ptr是一个普通指针const double *cptr = &PI;正确,cptr可以指向一个双精度常量*cptr = 3.14;//错误,不能给*ptr赋值

指针常量vs常量指针

指针常量

这是一个 指针常量(constant pointer)

int * const curErr = &errNumb;

含义:curErr 是一个指向 int 的常量指针
指针本身是常量:不能改变指针指向的对象(即不能让 curErr 指向别的变量)
但可以通过指针修改所指向对象的值(前提是原始对象不是常量)

*curErr = 20; // ✅ 允许修改 errNumb 的值int another = 42;
curErr = &another; // ❌ 错误!curErr 是 const 指针,不能指向其他地址

常量指针

const int * curErr = &errNumb;

含义:curErr 是一个指向 const int 的指针
所指向的内容是常量:不能通过这个指针修改它指向的对象的值
但是可以改变指针指向的对象(可以指向其他地址)

*curErr = 20; // ❌ 不允许修改,因为 curErr 指向的是 const int
int another = 42;
curErr = &another; // ✅ 允许,指针可以指向另一个地址

🚀 六、const 与 constexpr:常量表达式的进阶

① 常量表达式

值在编译期确定,且不可修改。

示例:const int max = 20;。

② constexpr

C++11 引入强制编译期计算,确保常量表达式。

语法:constexpr T var = expr;

constexpr int max = 20;
constexpr int limit = max + 10; // ✅ 编译期计算
// constexpr int sz = getSize(); // ❌ 运行时函数,非 constexpr
constexpr int getConstSize() { return 30; }
constexpr int sz = getConstSize(); // ✅ OK

🛠 示例代码

#include <iostream>
constexpr int max = 20;
constexpr int limit = max + 10;
constexpr int getConstSize() { return 30; }
constexpr int sz = getConstSize();
int main() {std::cout << "Max: " << max << std::endl;   // 输出 20std::cout << "Limit: " << limit << std::endl; // 输出 30std::cout << "Size: " << sz << std::endl;   // 输出 30return 0;
}

💡 实践建议

优先使用 constexpr:确保编译期常量,性能更优。

constexpr 函数:保持简单,确保编译期可计算。

全局变量:用 extern constexpr 共享常量

🧩总结

const 是 C++ 编程中不可或缺的工具,通过限制修改提升代码安全性、可读性和性能。从普通变量到指针、函数参数、成员函数、返回值,const 的应用无处不在。掌握 const 的正确用法,能显著提高你的 C++ 编程水平

如果你觉得本文对你有帮助,不妨点赞 + 收藏 + 关注,更多 C++ 系列教程将持续更新 🔥!

相关文章:

C++ const 修饰符深入浅出详解

C const 修饰符深入浅出详解 &#x1f4c5; 更新时间&#xff1a;2025年6月6日 &#x1f3f7;️ 标签&#xff1a;C | const关键字 | 常量 | 多文件编程 | 现代C 文章目录 前言&#x1f31f; 一、const 是什么&#xff1f;为什么要用&#xff1f;示例✅ const 的四大好处 &…...

Python 数据类型转换、编码处理与文件操作实战指南

一、数据类型转换 int (整型) 与 str (字符串) 之间&#xff1a; str 转 int&#xff1a;int("123") (要求字符串内容必须是数字)。 int 转 str&#xff1a;str(123)。 规则&#xff1a; 使用目标类型的英文名加括号包裹原数据即可。 list (列表) 与 tuple (元组…...

Readest(电子书阅读器) v0.9.53

Readest 是一款开源电子书阅读器&#xff0c;专为沉浸式和深度阅读体验而设计。它是对Foliate的现代重写&#xff0c;利用Next. js 15和Tauri v2在macOS、Windows、Linux和Web上提供无缝的跨平台体验&#xff0c;并即将支持移动平台。 软件特色 多格式支持 支持EPUB、MOBI、K…...

USART 串口通信全解析:原理、结构与代码实战

文章目录 USARTUSART简介USART框图USART基本结构数据帧起始位侦测数据采样波特率发生器串口发送数据 主要代码串口接收数据与发送数据主要代码 USART USART简介 一、USART 的全称与基本定义 英文全称 USART&#xff1a;Universal Synchronous Asynchronous Receiver Transmi…...

Matlab | matlab中的图像处理详解

MATLAB 图像处理详解 这里写目录标题图像处理 MATLAB 图像处理详解一、图像基础操作1. 图像读写与显示2. 图像信息获取3. 图像类型转换二、图像增强技术1. 对比度调整2. 去噪处理3. 锐化处理三、图像变换1. 几何变换2. 频域变换四、图像分割1. 阈值分割2. 边缘检测3. 区域分割五…...

UOS无法安装deb软件包

UOS无法安装deb软件包 问题描述解决办法: 关闭安全中心的应用隔离结果验证 问题描述 UOS安装Linux微信的deb包时&#xff0c;无法正常安装 解决办法: 关闭安全中心的应用隔离 要关闭-安全中心的应用隔离后才可以正常软件和运行。 应用安全----》 允许任意应用。 结果验证 # …...

VUE前端实现自动打包成压缩文件

VUE前端实现自动打包成压缩文件 背景思路实现打包代码实现 尾巴 背景 做前端开发的兄弟们都经历过每次开发完成之后发包需要进行打包&#xff0c;然后将打包文件压缩。每次打好包了都得手动压缩一遍&#xff0c;就有点繁琐。今天我们就使用一种命令行自动压缩的方法&#xff0…...

2025政务服务便民热线创新发展会议顺利召开,张晨博士受邀分享

5月28日&#xff0c;由新华社中国经济信息社、新华社广东分社联合主办的2025政务服务便民热线创新发展暨“人工智能热线”会议在广州举行。会议围绕“人工智能与新质热线”主题&#xff0c;邀请全国的12345政务服务便民热线主管部门负责人、省市热线负责人和专家学者&#xff0…...

【PDF PicKiller】PDF批量删除固定位置图片工具,默认解密,可去一般图、背景图、水印图!

PDF批量删除固定位置图片工具 PDF PicKiller <center>PDF PicKiller [Download](https://github.com/Peaceful-World-X/PDF-PicKiller)&#x1f929; 工具介绍&#x1f973; 主要功能&#x1f92a; 软件使用&#x1f92a; 参数解释&#x1f92a; 关键代码&#x1f929; 项…...

SpringAI Alibaba实战文生图

1️⃣ 前置准备&#xff1a;搭建开发环境与服务配置&#x1f680; &#x1f527; 1.1 环境要求 JDK 17&#xff08;推荐 JDK 21&#xff09;、Spring Boot 3.x&#xff08;本案例使用 3.3.4&#xff09;、阿里云百炼大模型服务 API Key。需在阿里云控制台完成服务开通并获取有…...

GIC700组件

GIC700包含了几个重要的组件,它们使用一个内部的GIC互联,用于在不同的组件之间使用AXI5-Stream接口进行路由。 1. Distributor(GICD) gicd是GIC700中所有组件之间的主要通信节点。它作为SPI的管理者以及维护LPI的cache,并且与其它chip上的GIC700组件进行通信。当支持GIC…...

几种简单的排序算法(C语言)

目录 1 简介 2 冒泡排序 2.1 基本思路 2.2 代码实现 3 选择排序 3.1 基本思路 3.2 代码实现 4 插入排序 4.1 基本思路 4.2 代码实现 5 快速排序 5.1 基本思路 5.2 代码实现 6 归并排序 6.1 基本思路 6.2 代码实现 7 基数排序 7.1 基本思路 7.2 代码实现 8 …...

RTOS学习之重难点

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…...

有没有 MariaDB 5.5.56 对应 MySQL CONNECTION_CONTROL 插件

有没有 MariaDB 对应 MySQL CONNECTION_CONTROL 插件 背景 写这篇文章的目的是因为昨晚半夜突然被call起来&#xff0c;有一套系统的mysql数据库启动失败了。尝试了重启服务器也不行。让我协助排查一下问题出在哪。 分析过程 一开始拿到服务器IP地址&#xff0c;就去数据库…...

setting up Activiti BPMN Workflow Engine with Spring Boot

spring.activiti.database-schema-update: true Controls how Activiti handles its database tables on startup. Options: true – Default. Creates or updates tables automatically if missing. ✅ Good for development. false – Disables auto-update. Throws an err…...

使用 C/C++ 和 OpenCV 提取图像的感兴趣区域 (ROI)

使用 C/C 和 OpenCV 提取图像的感兴趣区域 (ROI) 在计算机视觉中&#xff0c;感兴趣区域 (Region of Interest, ROI) 是指从图像中选择的一个特定区域&#xff0c;我们希望对其进行进一步的处理或分析。例如&#xff0c;在人脸识别中&#xff0c;ROI 就是包含人脸的矩形框。Op…...

TripGenie:畅游济南旅行规划助手:个人工作纪实(二十二)

这周&#xff0c;我进行了历史记录的设计与制作&#xff0c;我对于每一个用户与智能体交互得出的历史行程的数据进行了存储与可视化展示。 首先&#xff0c;我设置了一个csv文件存储每一个得出的行程规划&#xff0c;注意这里的地图我设置了一个全路径进行存储&#xff0c;这样…...

如何用AI高效运营1000+Tiktok矩阵账号

在当今数字化的时代&#xff0c;Tiktok 矩阵账号运营成为了众多企业和个人追求流量与变现的重要手段。然而&#xff0c;面对众多的账号管理&#xff0c;如何高效运营成为了关键。此时&#xff0c;AI 工具的出现为我们提供了强有力的支持。 一、Tiktok 矩阵账号的重要性 Tiktok…...

杭州瑞盟 MS35774/MS35774A 低噪声256细分微步进电机驱动,用于空调风门电机驱动,香薰电机驱动

杭州瑞盟 MS35774/MS35774A 低噪声256细分微步进电机驱动&#xff0c;用于空调风门电机驱动&#xff0c;香薰电机驱动 简述 MS35774/MS35774A 是一款高精度、低噪声的两相步进 电机驱动芯片&#xff0c;芯片内置功率 MOSFET &#xff0c;长时间工作的平均电 流可以达到 1…...

【论文解读】Toolformer: 语言模型自学使用工具

1st author: ‪Timo Schick‬ - ‪Google Scholar‬ paper: Toolformer: Language Models Can Teach Themselves to Use Tools | OpenReview NeurIPS 2023 oral code: lucidrains/toolformer-pytorch: Implementation of Toolformer, Language Models That Can Use Tools, by…...

408第一季 - 数据结构 - 线性表II

链表 头节点始终指向第一个 头节点的好处&#xff1a; 第一个好处 这里L是头节点 可以发现&#xff0c;删除第一个也可以统一了 第二个好处 这是无头节点&#xff0c;空和非空指向的不一样 然后有头节点就可以统一了&#xff01; 双链表 插入 第一步要在第四步之前&…...

网络通讯知识——通讯分层介绍,gRPC,RabbitMQ分层

网络通讯分层 网络通讯分层是为了将复杂的网络通信问题分解为多个独立、可管理的层次&#xff0c;每个层次专注于特定功能。目前主流的分层模型包括OSI七层模型和TCP/IP四层&#xff08;或五层&#xff09;模型&#xff0c;以下是详细解析&#xff1a; 一、OSI七层模型&#…...

Linux与Windows切换使用Obsidian,出现 unexplained changes 问题的解决

如果你的Obsidian文档在Linux与Windows间来回切换&#xff0c;可能会涉及到文件的保存换行符问题&#xff0c;但这样的话就容易导致一个问题&#xff0c;那就是内容无差异&#xff0c;Obsidian却提示unexplained changes&#xff0c;Windows系统下的解决方法如下&#xff0c;找…...

基于VMD-LSTM融合方法的F10.7指数预报

F10.7 Daily Forecast Using LSTM Combined With VMD Method ​​F10.7​​ solar radiation flux is a well-known parameter that is closely linked to ​​solar activity​​, serving as a key index for measuring the level of solar activity. In this study, the ​​…...

35 C 语言字符串转数值函数详解:strtof、strtod、strtold(含 errno 处理、ERANGE 错误)

1 strtof() 函数 1.1 函数原型 #include <stdlib.h> // 必须包含这个头文件才能使用 strtof() #include <errno.h> // 包含 errno 和 ERANGE #include <float.h> // 包含 FlOAT_MAX 和 FLOAT_MIN #include <math.h> // 包含 HUGE_VALF(inf)float…...

解决 idea提示`SQL dialect is not configured` 问题

前言 在 Java 开发中&#xff0c;尤其是使用 IntelliJ IDEA 或 MyBatis 等框架时&#xff0c;开发者常会遇到 SQL dialect is not configured 的警告或错误。这一问题不仅影响代码的高亮和智能提示功能&#xff0c;还可能导致表结构解析失败、语法校验失效等问题。 一、问题分…...

springboot的test模块使用Autowired注入失败

springboot的test模块使用Autowired注入失败的原因&#xff1a; 注入失败的原因可能是用了junit4的包的Test注解 import org.junit.Test;解决方法&#xff1a;再加上RunWith(SpringRunner.class)注解即可 或者把Test由junit4改成junit5的注解&#xff0c;就不用加上RunWith&…...

日志收集工具-Filebeat

提示&#xff1a;windows 环境下 Filebeat 的安装与使用 文章目录 前言一、安装二、配置部署三、启动测试 前言 Filebeat 一般用于日志采集&#xff0c;由两部分组成 &#xff1a;Harvesters 和 prospector Harvesters采集器&#xff1a;逐行读取单个文件的内容&#xff0c;并…...

【PCIe总线】 -- PCI、PCIe相关实现

PCI、PCIe相关概念和知识点 【PCIe总线】-- PCI、PCIe基础知识点整理 【PCIe】非常适合初学的pcie博客(PCIe知识整理) PCIe具体实现 【PCIe】如何获取PCIe的BAR空间大小&#xff1f;...

Vue3学习(4)- computed的使用

1. 简述与使用 作用&#xff1a;computed 用于基于响应式数据派生出新值&#xff0c;其值会自动缓存并在依赖变化时更新。 ​缓存机制​&#xff1a;依赖未变化时直接返回缓存值&#xff0c;避免重复计算&#xff08;通过 _dirty 标志位实现&#xff09;。​响应式更新​&…...