3. 常量与运算符:字面常量、const常量、算术运算符、赋值运算符

大家好,我是老刘。今天咱们聊聊C++里最基础、但也是最容易踩坑的两个东西——常量和运算符。你别看它们简单,我见过太多项目因为常量定义不规范、运算符优先级搞错,导致线上bug的案例。嗯,咱们一个一个来。

3.1 字面常量:代码里的“硬数字”

字面常量,说白了就是你直接写在代码里的那些值。比如 423.14'A'"hello"。它们就是字面意思,不能再变了。

常见字面常量类型:

类型示例说明
整型42 0xFF 077十进制、十六进制、八进制
浮点型3.14 1.2e5小数或科学计数法
字符型'A' '\n'单个字符,转义字符
字符串"hello"双引号括起来的字符序列
布尔型true falseC++11起推荐用

我个人习惯,能用 const 或枚举代替的字面常量,绝不直接写数字。为什么?你想想看,代码里突然冒出一个 86400,鬼知道它代表什么?是秒数?是行数?还是某个魔法数字?

我的小技巧: 项目中我经常用 using namespace std::literals 来写更语义化的字面量,比如 24h60s,代码可读性直接拉满。

3.2 const常量:让编译器帮你“盯”着

const 关键字,说白了就是告诉编译器:“这个值,你帮我看着,谁也别想改它。” 一旦初始化,后面再赋值就编译报错。

const int MAX_USERS = 1000;
// MAX_USERS = 2000;  // 编译错误!const变量不能修改

const double PI = 3.1415926;
double radius = 5.0;
double area = PI * radius * radius;

我在项目中遇到过最经典的坑:有人把 const 变量定义在头文件里,结果多个cpp文件包含后,链接时报重复定义。嗯,这里要注意——const 变量默认是内部链接的,但如果加了 extern,就要小心了。

避坑指南: 我曾经在维护一个老项目时,发现有人用 #define PI 3.14 而不是 const double PI = 3.14。结果宏定义没有类型检查,在表达式里隐式转换出了大问题。记住:C++里能用 const 就别用 #define

3.3 算术运算符:加减乘除那些事儿

算术运算符大家都熟:+ - * / %。但有几个细节,新手经常翻车。

  • 整数除法会截断: 5 / 2 结果是 2,不是 2.5。想要浮点结果,至少一个操作数得是浮点型。
  • 取模 % 只用于整数: 5.5 % 2 编译报错。浮点数取余用 fmod()
  • 自增自减的前置后置: ++i 先加后用,i++ 先用后加。性能上,前置略优(少一次临时对象构造)。
int a = 10, b = 3;
int c = a / b;   // c = 3
int d = a % b;   // d = 1

double e = 10.0 / 3;  // e ≈ 3.3333

int x = 5;
int y = ++x;  // x=6, y=6
int z = x++;  // x=7, z=6

我的经验: 在循环里,我习惯用 ++i 而不是 i++。虽然现代编译器优化后两者性能一样,但这是个好习惯。另外,别在同一个表达式里多次修改同一个变量,比如 i = i++ + ++i——这是未定义行为,不同编译器结果可能不同。

3.4 赋值运算符:不只是 =

赋值运算符 = 是最常用的,但C++还提供了一组复合赋值运算符:+= -= *= /= %= 等。它们能让代码更简洁,也更容易读。

int score = 100;
score += 10;   // 等价于 score = score + 10;  score=110
score -= 20;   // score=90
score *= 2;    // score=180
score /= 3;    // score=60
score %= 7;    // score=4

赋值运算符有个容易忽略的点:赋值表达式本身也有值。比如 a = b = c = 5 是合法的,从右向左依次赋值。但我不建议这么写,可读性太差。

避坑指南: 我曾经在代码审查时看到有人写 if (x = 5),本意是判断 x == 5,结果写成了赋值。赋值表达式返回的是赋的值(非零即为true),所以条件永远成立。这个bug藏了两个月才被发现。我的建议是:如果确实要写赋值+判断,显式写成 if ((x = getValue()) != 0),让意图一目了然。

3.5 知识体系总览

下面这张图,是我自己梳理的本章知识结构。你可以把它当作一个快速索引,哪里忘了就回来瞄一眼。

常量与运算符 常量 字面常量 const常量 整型、浮点、字符、 字符串、布尔 不可修改 类型安全 运算符 算术运算符 赋值运算符 + - * / % ++ -- = += -= *= /= %= ⚠ 常见陷阱 整数除法截断 | const vs #define | 赋值与判断混淆 自增自减副作用 | 复合赋值优先级

3.6 综合示例:计算圆的周长和面积

最后,咱们来个综合小例子,把今天讲的东西串起来。这个例子我在培训新人时经常用,简单但能覆盖不少知识点。

#include <iostream>
using namespace std;

int main() {
    // const常量定义
    const double PI = 3.1415926;
    
    // 字面常量 + 变量
    double radius = 5.0;
    
    // 算术运算符
    double circumference = 2 * PI * radius;
    double area = PI * radius * radius;
    
    // 复合赋值运算符
    radius += 1.0;  // 半径增加1
    
    cout << "原始半径: 5.0" << endl;
    cout << "周长: " << circumference << endl;
    cout << "面积: " << area << endl;
    cout << "增加后的半径: " << radius << endl;
    
    return 0;
}

输出结果:

原始半径: 5.0
周长: 31.4159
面积: 78.5398
增加后的半径: 6

你看,PIconst 定义,保证了它不会被意外修改。2 * PI * radius 里混合了字面常量、const 常量和变量。最后 radius += 1.0 是复合赋值运算符的典型用法。代码简洁、意图清晰,这就是好的C++风格。

最后说一句: 常量和运算符是C++的“砖瓦”,看似简单,但用好了能让代码既安全又易读。我见过太多项目因为常量定义混乱、运算符滥用导致维护成本飙升。记住:写代码是给人看的,顺便让机器执行。多用 const,少写魔法数字,运算符别炫技——这是我从多年项目里总结出来的血泪教训。

公众号:蓝海资料掘金营,微信deep3321