const 在 C/C++ 中的全面用法(C/C++ 差异+核心场景+实战示例)

const 在 C/C++ 中的全面用法(C/C++ 差异+核心场景+实战示例)

const 是 C/C++ 中的只读修饰符,核心作用是限定变量/对象/函数等不可被修改,既能提升代码可读性、避免意外修改,又能让编译器做更多优化(如常量折叠),还能增强类型安全。C 和 C++ 对 const 的支持有核心差异,C++ 在 C 的基础上做了大幅扩展,使其适配面向对象、模板等特性。下面按「基础通用用法」「C 专属特性」「C++ 增强用法」「核心差异」「实战注意事项」展开,覆盖所有高频场景。

一、基础通用用法(C/C++ 均支持,核心共性)

这是 const 最基础的用法,C 和 C++ 规则完全一致,核心是修饰变量为只读,不可直接赋值修改

1. 修饰普通变量(只读变量)

// 定义只读变量,初始化后不可修改constint a =10;// a = 20; // 编译报错:只读变量不可赋值int b = a;// 合法:可以读取const变量的值
  • 关键:const 变量必须在定义时初始化(否则后续无法赋值,变成“只读的未初始化变量”,无意义);
  • 本质:const 普通变量是只读的内存变量(非编译期常量,C 中尤为明显),只是编译器禁止直接修改,底层仍占用内存。

2. 修饰指针(3 种经典场景,C/C++ 完全一致)

指针的核心是「指针本身」和「指针指向的内容」,const 修饰不同位置,效果完全不同,这是高频考点,记准就近修饰原则const 离谁近,就限定谁不可修改。

int x =10, y =20;// 场景1:const 修饰*p → 指针指向的内容不可修改(只读内容)constint* p =&x;// *p = 30; // 编译报错:指向的内容只读 p =&y;// 合法:指针本身可以指向其他地址// 场景2:const 修饰p → 指针本身不可修改(只读指针,地址固定)int*const p =&x;*p =30;// 合法:指向的内容可以修改// p = &y; // 编译报错:指针本身地址不可变// 场景3:const 同时修饰*p和p → 内容+指针均不可修改constint*const p =&x;// *p = 30; // 报错:内容只读// p = &y; // 报错:指针地址只读

记忆技巧:把 * 理解为「指向的内容」,从右往左读:

  • const int* pp 是指针,指向 const int(只读int);
  • int* const ppconst 指针,指向 int

3. 修饰函数参数(C/C++ 通用优化手段)

用于限定函数内不可修改传入的参数,避免意外修改实参(尤其针对指针/引用参数,防止篡改原数据),同时明确函数语义:“此参数仅做读取,不做修改”。

// 示例1:修饰普通参数(值传递,意义较小,因为值传递是拷贝,修改不影响实参)voidprint(constint num){// num = 100; // 报错:不可修改printf("%d\n", num);}// 示例2:修饰指针参数(核心场景,防止修改指向的原数据)voidmodify(constint* arr,int len){// arr[0] = 10; // 报错:不可修改原数组for(int i=0; i<len; i++){printf("%d ", arr[i]);// 仅读取,合法}}intmain(){int arr[]={1,2,3};modify(arr,3);// 实参不会被篡改,安全return0;}
  • 最佳实践:指针/引用参数尽量加 const(只要函数内不修改),这是工业界代码的通用规范;
  • 普通值参数加 const 意义不大(拷贝开销小,且修改不影响实参),可根据代码规范选择。

二、C 语言中 const 的专属特性(C++ 中已优化/改变)

C 语言对 const 的支持比较“简陋”,核心特点是const 变量不是真正的编译期常量,仅为“只读变量”,这是和 C++ 最核心的早期差异。

1. const 变量不可作为数组长度(C99 变长数组除外)

C 中 const 变量占用内存,编译器不会将其视为“常量表达式”,因此不能用于定义固定长度数组:

constint N =5;// int arr[N]; // C89 编译报错:N 不是编译期常量(C99 支持变长数组 VLA,可运行但非标准固定数组)int arr[5];// 合法:字面量是编译期常量

2. const 变量需用 extern 显式声明才能跨文件使用

C 中 const 变量的默认链接属性是内部链接(仅当前文件可见),若要在其他文件使用,必须加 extern 显式声明:

// file1.c:定义跨文件使用的const变量,必须加externexternconstint PI =3.1415;// file2.c:使用前声明externconstint PI;printf("PI = %f\n", PI);// 合法

若不加 extern,C 编译器会为每个文件生成独立的 const 变量,导致链接冲突或值不一致。

3. 无引用特性,const 仅能修饰变量/指针/函数参数

C 语言没有引用(&) 特性,因此 const 无法修饰引用,仅能作用于变量、指针、函数参数,用法远少于 C++。

三、C++ 中 const 的增强用法(C 无此特性,面向对象核心)

C++ 完全兼容 C 中 const 的基础用法,同时针对面向对象、引用、模板、函数做了大幅扩展,使 const 成为实现代码健壮性的核心关键字,这部分是 C++ 开发的重点。

1. const 修饰引用(常引用,核心场景)

C++ 引入引用后,const 可修饰引用为常引用(const &),核心作用:

  • 绑定只读变量/右值(普通引用只能绑定可修改的左值);
  • 避免拷贝大对象(如自定义类、字符串),同时保证原对象不被修改。
#include<iostream>#include<string>usingnamespace std;intmain(){constint a =10;// int& ref = a; // 编译报错:普通引用不能绑定const左值constint& ref1 = a;// 合法:常引用绑定const左值constint& ref2 =20;// 合法:常引用绑定右值(C++ 编译器会生成临时变量)// 大对象场景:常引用避免拷贝,提升性能const string& s ="hello world";// 避免生成临时string拷贝 cout << s << endl;return0;}
  • 最佳实践:函数返回大对象/传递大对象时,优先使用 const &,既避免拷贝开销,又保证原对象安全。

2. const 修饰类的成员函数(常成员函数,面向对象核心)

这是 C++ 面向对象的核心特性const 放在类成员函数的参数列表后、函数体前,表示该函数是常成员函数,核心规则:

  • 常成员函数不能修改类的任何非静态成员变量
  • 常成员函数只能调用类的其他常成员函数/静态成员函数(不能调用普通成员函数,防止间接修改成员);
  • 常对象(const 修饰的类对象)只能调用常成员函数(普通对象可调用常/普通成员函数)。
#include<iostream>usingnamespace std;classPerson{private: string name;int age;staticint count;// 静态成员变量public:Person(string n,int a):name(n),age(a){ count++;}// 常成员函数:不能修改非静态成员,参数列表后加const string getName()const{// age = 20; // 编译报错:常成员函数不能修改非静态成员return name;}// 普通成员函数:可修改非静态成员voidsetAge(int a){ age = a;}// 静态成员函数:无this指针,可被常/普通对象调用staticintgetCount(){return count;}};int Person::count =0;// 静态成员变量类外初始化intmain(){const Person p1("张三",18);// 常对象// p1.setAge(20); // 报错:常对象不能调用普通成员函数 cout << p1.getName()<< endl;// 合法:调用常成员函数 cout << p1.getCount()<< endl;// 合法:调用静态成员函数 Person p2("李四",20);// 普通对象 p2.setAge(25);// 合法:调用普通成员函数 cout << p2.getName()<< endl;// 合法:普通对象可调用常成员函数return0;}
  • 核心原理:常成员函数的this 指针const 类名* const 类型(指向的对象只读+指针本身只读),因此无法修改成员变量;
  • 最佳实践:类中仅做读取操作的成员函数,必须加 const(如获取属性的 get 方法),这是 C++ 类设计的通用规范。

3. const 修饰类的对象/成员变量

(1)const 修饰类的对象(常对象)

类的常对象所有非静态成员变量均不可修改,仅能调用常成员函数/静态成员函数(如上例中的 p1),本质是限制对象的修改权限。

(2)const 修饰类的成员变量(常成员变量)

类的常成员变量必须在构造函数的「初始化列表」中初始化(不能在函数体内赋值),且初始化后终身不可修改

classCircle{private:constdouble PI;// 常成员变量:圆周率,不可修改int radius;public:// 必须在初始化列表中初始化常成员变量Circle(int r):PI(3.1415),radius(r){}doublegetArea()const{return PI * radius * radius;// 仅读取,合法}};
  • 注意:常成员变量无法通过普通成员函数修改,即使是类的内部方法也不行。

4. const 修饰函数返回值

C++ 中 const 可修饰函数返回值,根据返回值类型(普通类型/指针/引用)有不同作用,核心是禁止对返回值直接赋值/修改

#include<iostream>usingnamespace std;// 场景1:返回普通类型(int),const 意义不大(返回值是拷贝,修改不影响原数据)constintadd(int a,int b){return a + b;}// 场景2:返回指针,const 限定指向的内容不可修改constint*getArr(){staticint arr[]={1,2,3};return arr;}// 场景3:返回引用,const 限定引用的对象不可修改(核心场景,避免原对象被篡改)constint&getMax(int& a,int& b){return a > b ? a : b;}intmain(){// add(3,5) = 10; // 报错:const返回值不可赋值constint* p =getArr();// *p = 100; // 报错:指向的内容只读int x =5, y =10;// getMax(x,y) = 20; // 报错:常引用返回值不可修改 cout <<getMax(x,y)<< endl;// 仅读取,合法return0;}
  • 最佳实践:返回指针/引用时,若不想让调用方修改原对象,必须加 const 修饰返回值。

5. const 作为编译期常量(C++ 核心优化)

C++ 中,初始化值为常量表达式的 const 变量会被编译器视为编译期常量(而非只读内存变量),可用于:

  • 定义固定长度数组;
  • 作为模板参数;
  • 作为枚举常量、类的静态常成员初始化。
#include<iostream>#include<vector>usingnamespace std;// const 编译期常量,可用于数组长度constint N =5;int arr[N]={1,2,3,4,5};// 合法:C++ 编译期确定长度// 作为模板参数(模板参数必须是编译期常量) vector<int>v(N);// 合法:N 是编译期常量// 类的静态常成员变量:可直接类内初始化(编译期常量)classMath{public:staticconstdouble PI;// 声明staticconstint MAX =100;// 类内直接初始化(编译期常量)};constdouble Math::PI =3.1415;// 类外初始化(若需不同值)intmain(){ cout << Math::MAX << endl;// 100return0;}

这一特性解决了 C 中 const 变量不能作为数组长度的问题,是 C++ 对 const 的关键优化。

6. const 修饰模板参数(C++ 模板特性)

C++ 模板中,const 可修饰模板参数,限定模板实例化后的参数为只读,适配不同的常量类型需求:

template<constint N>classArray{public:int data[N];voidprint(){for(int i=0; i<N; i++){ cout << data[i]<<" ";}}};intmain(){ Array<5> arr;// 实例化:N=5(编译期常量) arr.data[0]=1; arr.print();return0;}

四、C/C++ 中 const 的核心差异总结(必记)

特性/场景C 语言C++ 语言
常量属性仅为只读变量(占内存)初始化值为常量表达式时是编译期常量,否则为只读变量
数组长度不可用于固定数组长度(C99 VLA 除外)可直接用于固定数组长度
跨文件使用默认内部链接,需加 extern 显式声明全局 const 可直接跨文件使用(编译器自动优化)
引用修饰无引用特性,不可修饰支持常引用(const &),可绑定左值/右值
类成员函数无类特性,不可修饰支持常成员函数,限制修改成员变量
类常对象/成员变量无类特性,不可修饰支持修饰常对象、常成员变量(初始化列表初始化)
函数返回值/模板仅支持基础类型,无扩展支持修饰指针/引用返回值、模板参数

五、const 实战注意事项(避坑指南)

1. const 不是绝对的“只读”——可通过指针强制修改(不推荐)

C/C++ 中,const 变量的只读性是编译器层面的限制,底层仍占用内存,可通过「非 const 指针」强制修改(即const _cast,C++ 专用),但这是未定义行为,极易导致程序崩溃,严禁在生产代码中使用:

// C 语言:强制类型转换修改constint a =10;int* p =(int*)&a;*p =20;// 编译器不报错,但运行结果未定义(可能修改成功,也可能触发段错误)printf("a = %d\n", a);// 结果不可预测// C++ 语言:const_cast 转换(专用强制转换,仍不推荐)constint b =20;int* q = const_cast<int*>(&b);*q =30;// 未定义行为
  • 原则:一旦定义 const 变量,就不要尝试修改,违背 const 设计初衷。

2. 函数参数中,指针/引用优先加 const

针对自定义类、数组、字符串等大对象/复杂对象,传递参数时优先使用 const 指针/const 引用,既避免拷贝开销,又保证原对象不被修改,这是工业界 C/C++ 代码的通用规范。

3. C++ 类中,get 方法必须加 const,set 方法不加

类的属性访问方法(getXxx)仅做读取操作,必须声明为常成员函数;修改方法(setXxx)做写入操作,声明为普通成员函数,这是 C++ 类设计的黄金法则

4. 全局 const 变量尽量避免重复定义

C 中全局 const 需加 extern 统一定义,C++ 中虽可直接跨文件使用,但仍建议在头文件中声明,源文件中定义,避免多文件包含时的重复定义问题:

// math.h(头文件)#pragmaonceexternconstdouble PI;// 声明// math.cpp(源文件)#include"math.h"constdouble PI =3.1415;// 定义

5. const 与 constexpr 的区别(C++11 及以上)

C++11 引入 constexpr(编译期常量),很多开发者会混淆 constconstexpr,核心区别:

  • const只读约束,可能是编译期常量(初始化值为常量表达式),也可能是运行时常量(初始化值为变量);
  • constexpr强制编译期常量,初始化值必须是常量表达式,编译器在编译期就确定其值,可用于更多编译期场景(如 constexpr 函数、类的构造函数)。
constint a =10;// 编译期常量int n =5;constint b = n;// 运行时常量(只读变量)constexprint c =20;// 强制编译期常量// constexpr int d = n; // 编译报错:n 是变量,非常量表达式
  • 建议:C++11 及以上版本,明确需要编译期常量时用 constexpr仅需要只读约束时用 const

六、总结(核心要点浓缩)

  1. 核心作用const 是只读修饰符,C/C++ 通用,用于限定变量/对象不可修改,提升代码健壮性和编译器优化能力;
  2. C 语言特性:const 仅为只读变量,不可做数组长度,无引用/类相关用法,跨文件需加 extern
  3. C++ 增强特性:兼容 C 基础用法,新增常引用、常成员函数、常对象/常成员变量,const 可作为编译期常量,支持修饰函数返回值/模板参数;
  4. 关键规则
    • 指针:const 就近修饰,离谁近限定谁不可修改;
    • 类:常成员函数不能修改非静态成员,常对象只能调用常成员函数;
    • 引用:常引用可绑定左值/右值,是避免大对象拷贝的最佳实践;
  5. 避坑原则:不通过指针强制修改 const 变量,函数参数中指针/引用优先加 const,C++ 类中 get 方法加 const;
  6. C++11+ 补充constexpr 是强制编译期常量,与 const 互补,按需选择。

const 是 C/C++ 开发中使用频率最高的关键字之一,掌握其在 C/C++ 中的差异和各场景用法,是写出高质量、健壮性代码的基础,尤其在 C++ 面向对象开发中,const 的正确使用是衡量代码规范的重要标准。

Read more

【数据结构】哈希扩展学习

【数据结构】哈希扩展学习

目录 1. 位图 1.1 位图相关面试题 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。(本题为腾讯/百度等公司出过的一个面试题) 1.2 位图的设计及实现 1.3 C++库中的位图 bitset 1.4 位图的优缺点 1.5 位图相关考察题目 • 给定100亿个整数,设计算法找到只出现一次的整数? • 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集? • 一个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的所有整数 2. 布隆过滤器 2.1 什么是布隆过滤器 2.2 布隆过滤器器误判率推导 2.3 布隆过滤器代码实现 2.4 布隆过滤器删除问题 2.

By Ne0inhk

一文吃透 DFS 深度优先搜索:从原理到实战

在算法世界中,深度优先搜索(Depth-First Search,简称 DFS)是一种基础且核心的遍历与搜索算法。它以 “纵向挖掘” 为核心思想,沿着一条路径探索至终点后,回溯至前一节点继续探索其他路径,如同迷宫中沿着一条通道走到头,再折返寻找新通道。这种特性让 DFS 在图论遍历、迷宫求解、组合问题、拓扑排序等场景中广泛应用,也是面试与算法竞赛的高频考点。本文将从原理、实现、优化到实战,带你全面掌握 DFS。 一、DFS 核心原理:纵向探索与回溯 1. 核心思想 DFS 的本质是 “不撞南墙不回头”:从起始节点出发,优先探索当前节点的某一条邻接路径,直至无法继续前进(到达终点或已访问节点),再回溯到上一个未探索完所有邻接节点的节点,重复此过程,直至遍历所有可达节点。 2. 关键概念 * 访问标记:为避免重复访问节点(陷入死循环),需用数组或哈希表记录节点是否已被访问。 * 回溯:探索完一条路径后,

By Ne0inhk
基于纯verilogFPGA的双线性差值视频缩放 功能:利用双线性差值算法,pc端HDMI输入...

基于纯verilogFPGA的双线性差值视频缩放 功能:利用双线性差值算法,pc端HDMI输入...

基于纯verilogFPGA的双线性差值视频缩放 功能:利用双线性差值算法,pc端HDMI输入视频缩小或放大,然后再通过HDMI输出显示,可以任意缩放。 缩放模块仅含有ddr ip,手写了 ram,fifo 代码,可以较为轻松地移植到其他平台。 硬件平台:易灵思 ti60f225 EDA平台:efinity 基于FPGA的高效视频缩放系统设计与实现 ============================================ 一、项目定位 本项目在易灵思 Ti60F225 钛金系列 FPGA 上实现“端到端” 4K@60 视频缩放链路,目标是把任意分辨率(640×480–3840×2160)的 HDMI 输入实时缩放到用户指定分辨率,并通过 HDMI 输出。整个链路不依赖外部 DDR,仅使用片内 6.3 Mbit 嵌入式 SRAM 完成行缓存,

By Ne0inhk
《算法题讲解指南:优选算法-二分查找》--21.山峰数组的的峰顶索引,22.寻找峰值

《算法题讲解指南:优选算法-二分查找》--21.山峰数组的的峰顶索引,22.寻找峰值

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 21. 山峰数组的的峰顶索引 题目链接: 题目描述: 题目示例: 解法(二分查找): 算法思路: C++算法代码: 算法总结及流程解析: 22. 寻找峰值 题目链接: 题目描述: 题目示例: 解法(二分查找): 算法思路: C++算法代码: 算法总结及流程解析: 结束语 21. 山峰数组的的峰顶索引 题目链接: 852. 山脉数组的峰顶索引 - 力扣(LeetCode) 题目描述: 题目示例: 解法(

By Ne0inhk