跳到主要内容C/C++ 中 const 关键字的用法与差异详解 | 极客日志C++
C/C++ 中 const 关键字的用法与差异详解
综述由AI生成const 是 C/C++ 中的只读修饰符,用于限定变量或对象不可修改。文章对比了 C 与 C++ 的差异:C 中 const 仅为只读变量,不支持引用和类特性;C++ 支持常引用、常成员函数及编译期常量优化。涵盖基础用法(变量、指针)、C 专属限制(数组长度、extern)、C++ 增强(类、模板、返回值)及实战注意事项(如 const_cast 风险)。掌握 const 用法对提升代码健壮性和性能至关重要。
无尘942 浏览 const 在 C/C++ 中的全面用法
const 是 C/C++ 中的只读修饰符,核心作用是限定变量/对象/函数等不可被修改,既能提升代码可读性、避免意外修改,又能让编译器做更多优化(如常量折叠),还能增强类型安全。C 和 C++ 对 const 的支持有核心差异,C++ 在 C 的基础上做了大幅扩展,使其适配面向对象、模板等特性。下面按「基础通用用法」「C 专属特性」「C++ 增强用法」「核心差异」「实战注意事项」展开,覆盖所有高频场景。
一、基础通用用法(C/C++ 均支持,核心共性)
这是 const 最基础的用法,C 和 C++ 规则完全一致,核心是修饰变量为只读,不可直接赋值修改。
1. 修饰普通变量(只读变量)
const int a = 10;
int b = a;
- 关键:
const 变量必须在定义时初始化(否则后续无法赋值,变成'只读的未初始化变量',无意义);
- 本质:const 普通变量是只读的内存变量(非编译期常量,C 中尤为明显),只是编译器禁止直接修改,底层仍占用内存。
2. 修饰指针(3 种经典场景,C/C++ 完全一致)
指针的核心是「指针本身」和「指针指向的内容」,const 修饰不同位置,效果完全不同,这是高频考点,记准就近修饰原则:const 离谁近,就限定谁不可修改。
int x = 10, y = 20;
const int* p = &x;
p = &y;
int* const p = &x;
*p = 30;
const int* const p = &x;
记忆技巧:把 * 理解为「指向的内容」,从右往左读:
const int* p → p 是指针,指向 const int(只读 int);
int* const p → p 是 const 指针,指向 int。
3. 修饰函数参数(C/C++ 通用优化手段)
用于限定函数内不可修改传入的参数,避免意外修改实参(尤其针对指针/引用参数,防止篡改原数据),同时明确函数语义:'此参数仅做读取,不做修改'。
void print(const int num) {
printf("%d\n", num);
}
void modify(const int* arr, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
}
int main() {
int arr[] = {1, 2, 3};
modify(arr, 3);
return 0;
}
- 最佳实践:指针/引用参数尽量加
const(只要函数内不修改),这是工业界代码的通用规范;
- 普通值参数加
const 意义不大(拷贝开销小,且修改不影响实参),可根据代码规范选择。
二、C 语言中 const 的专属特性(C++ 中已优化/改变)
C 语言对 const 的支持比较'简陋',核心特点是const 变量不是真正的编译期常量,仅为'只读变量',这是和 C++ 最核心的早期差异。
1. const 变量不可作为数组长度(C99 变长数组除外)
C 中 const 变量占用内存,编译器不会将其视为'常量表达式',因此不能用于定义固定长度数组:
const int N = 5;
int arr[5];
2. const 变量需用 extern 显式声明才能跨文件使用
C 中 const 变量的默认链接属性是内部链接(仅当前文件可见),若要在其他文件使用,必须加 extern 显式声明:
extern const int PI = 3.1415;
extern const int 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>
using namespace std;
int main() {
const int a = 10;
const int& ref1 = a;
const int& ref2 = 20;
const string& s = "hello world";
cout << s << endl;
return 0;
}
- 最佳实践:函数返回大对象/传递大对象时,优先使用
const &,既避免拷贝开销,又保证原对象安全。
2. const 修饰类的成员函数(常成员函数,面向对象核心)
这是 C++ 面向对象的核心特性:const 放在类成员函数的参数列表后、函数体前,表示该函数是常成员函数,核心规则:
- 常成员函数不能修改类的任何非静态成员变量;
- 常成员函数只能调用类的其他常成员函数/静态成员函数(不能调用普通成员函数,防止间接修改成员);
- 常对象(const 修饰的类对象)只能调用常成员函数(普通对象可调用常/普通成员函数)。
#include <iostream>
using namespace std;
class Person {
private:
string name;
int age;
static int count;
public:
Person(string n, int a) : name(n), age(a) { count++; }
string getName() const {
return name;
}
void setAge(int a) { age = a; }
static int getCount() { return count; }
};
int Person::count = 0;
int main() {
const Person p1("张三", 18);
cout << p1.getName() << endl;
cout << p1.getCount() << endl;
Person p2("李四", 20);
p2.setAge(25);
cout << p2.getName() << endl;
return 0;
}
- 核心原理:常成员函数的this 指针是
const 类名* const 类型(指向的对象只读 + 指针本身只读),因此无法修改成员变量;
- 最佳实践:类中仅做读取操作的成员函数,必须加
const(如获取属性的 get 方法),这是 C++ 类设计的通用规范。
3. const 修饰类的对象/成员变量
(1)const 修饰类的对象(常对象)
类的常对象所有非静态成员变量均不可修改,仅能调用常成员函数/静态成员函数(如上例中的 p1),本质是限制对象的修改权限。
(2)const 修饰类的成员变量(常成员变量)
类的常成员变量必须在构造函数的「初始化列表」中初始化(不能在函数体内赋值),且初始化后终身不可修改:
class Circle {
private:
const double PI;
int radius;
public:
Circle(int r) : PI(3.1415), radius(r) {}
double getArea() const {
return PI * radius * radius;
}
};
- 注意:常成员变量无法通过普通成员函数修改,即使是类的内部方法也不行。
4. const 修饰函数返回值
C++ 中 const 可修饰函数返回值,根据返回值类型(普通类型/指针/引用)有不同作用,核心是禁止对返回值直接赋值/修改:
#include <iostream>
using namespace std;
const int add(int a, int b) {
return a + b;
}
const int* getArr() {
static int arr[] = {1, 2, 3};
return arr;
}
const int& getMax(int& a, int& b) {
return a > b ? a : b;
}
int main() {
const int* p = getArr();
int x = 5, y = 10;
cout << getMax(x, y) << endl;
return 0;
}
- 最佳实践:返回指针/引用时,若不想让调用方修改原对象,必须加
const 修饰返回值。
5. const 作为编译期常量(C++ 核心优化)
C++ 中,初始化值为常量表达式的 const 变量会被编译器视为编译期常量(而非只读内存变量),可用于:
- 定义固定长度数组;
- 作为模板参数;
- 作为枚举常量、类的静态常成员初始化。
#include <iostream>
#include <vector>
using namespace std;
const int N = 5;
int arr[N] = {1, 2, 3, 4, 5};
vector<int> v(N);
class Math {
public:
static const double PI;
static const int MAX = 100;
};
const double Math::PI = 3.1415;
int main() {
cout << Math::MAX << endl;
return 0;
}
这一特性解决了 C 中 const 变量不能作为数组长度的问题,是 C++ 对 const 的关键优化。
6. const 修饰模板参数(C++ 模板特性)
C++ 模板中,const 可修饰模板参数,限定模板实例化后的参数为只读,适配不同的常量类型需求:
template<const int N>
class Array {
public:
int data[N];
void print() {
for (int i = 0; i < N; i++) {
cout << data[i] << " ";
}
}
};
int main() {
Array<5> arr;
arr.data[0] = 1;
arr.print();
return 0;
}
四、C/C++ 中 const 的核心差异总结(必记)
| 特性/场景 | C 语言 | C++ 语言 |
|---|
| 常量属性 | 仅为只读变量(占内存) | 初始化值为常量表达式时是编译期常量,否则为只读变量 |
| 数组长度 | 不可用于固定数组长度(C99 VLA 除外) | 可直接用于固定数组长度 |
| 跨文件使用 | 默认内部链接,需加 extern 显式声明 | 全局 const 可直接跨文件使用(编译器自动优化) |
| 引用修饰 | 无引用特性,不可修饰 | 支持常引用(const &),可绑定左值/右值 |
| 类成员函数 | 无类特性,不可修饰 | 支持常成员函数,限制修改成员变量 |
| 类常对象/成员变量 | 无类特性,不可修饰 | 支持修饰常对象、常成员变量(初始化列表初始化) |
| 函数返回值/模板 | 仅支持基础类型,无扩展 | 支持修饰指针/引用返回值、模板参数 |
五、const 实战注意事项(避坑指南)
1. const 不是绝对的'只读'——可通过指针强制修改(不推荐)
C/C++ 中,const 变量的只读性是编译器层面的限制,底层仍占用内存,可通过「非 const 指针」强制修改(即const_cast,C++ 专用),但这是未定义行为,极易导致程序崩溃,严禁在生产代码中使用:
const int a = 10;
int* p = (int*)&a;
*p = 20;
printf("a = %d\n", a);
const int 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++ 中虽可直接跨文件使用,但仍建议在头文件中声明,源文件中定义,避免多文件包含时的重复定义问题:
#pragma once
extern const double PI;
#include "math.h"
const double PI = 3.1415;
5. const 与 constexpr 的区别(C++11 及以上)
C++11 引入 constexpr(编译期常量),很多开发者会混淆 const 和 constexpr,核心区别:
const:只读约束,可能是编译期常量(初始化值为常量表达式),也可能是运行时常量(初始化值为变量);
constexpr:强制编译期常量,初始化值必须是常量表达式,编译器在编译期就确定其值,可用于更多编译期场景(如 constexpr 函数、类的构造函数)。
const int a = 10;
int n = 5;
const int b = n;
constexpr int c = 20;
- 建议:C++11 及以上版本,明确需要编译期常量时用
constexpr,仅需要只读约束时用 const。
六、总结(核心要点浓缩)
- 核心作用:
const 是只读修饰符,C/C++ 通用,用于限定变量/对象不可修改,提升代码健壮性和编译器优化能力;
- C 语言特性:const 仅为只读变量,不可做数组长度,无引用/类相关用法,跨文件需加
extern;
- C++ 增强特性:兼容 C 基础用法,新增常引用、常成员函数、常对象/常成员变量,const 可作为编译期常量,支持修饰函数返回值/模板参数;
- 关键规则:
- 指针:
const 就近修饰,离谁近限定谁不可修改;
- 类:常成员函数不能修改非静态成员,常对象只能调用常成员函数;
- 引用:常引用可绑定左值/右值,是避免大对象拷贝的最佳实践;
- 避坑原则:不通过指针强制修改 const 变量,函数参数中指针/引用优先加 const,C++ 类中 get 方法加 const;
- C++11+ 补充:
constexpr 是强制编译期常量,与 const 互补,按需选择。
const 是 C/C++ 开发中使用频率最高的关键字之一,掌握其在 C/C++ 中的差异和各场景用法,是写出高质量、健壮性代码的基础,尤其在 C++ 面向对象开发中,const 的正确使用是衡量代码规范的重要标准。
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online