第二章 变量与类型:让程序记住东西

← 返回目录

本章目标:搞懂整数、浮点数、布尔、字符串、auto、常量。 这是全书的基石,请务必动手验证每个例子。

2.1 变量:给数据起个名字

变量就是"盒子上贴了标签"。声明变量 = 声明盒子和标签:

int age = 18;            // int 是"整数"类型
age = 19;                // 把盒子里的值改成 19
std::println("{}", age); // 打印 19

格式:类型 名字 = 初值;

2.2 整数类型(重点记忆)

类型大小(常用平台)取值范围
int4 字节(32bit)约 ±21 亿 (2^31-1)
unsigned4 字节0 ~ 约 42 亿
long8 字节(64bit)极大,写大数用它
long long8 字节同 long 差不多,明确就是 8 字节
short2 字节约 ±3.2 万
char1 字节存一个字符/小整数,-128~127

注意:

  1. 范围是"常用平台",不要背,用的时候查。
  2. 溢出(超出范围)不会报错,而是"绕一圈":
   int x = 2147483647;   // int 最大值
   x = x + 1;            // 变成 -2147483648!悄无声息

这是 C++ 最经典的坑之一,以后做计算要想着范围。

  1. unsigned 表示"无符号"(只能非负)。数字超过 int 范围、

保证不会为负时再用它。新手别乱用,容易出 bug。

2.3 浮点数(小数)

double price = 3.14;   // 双精度,绝大多数情况用它
float  f = 2.5f;       // 单精度,省内存但精度低

规则:

需要比较时用"差小于一个极小值":

  double a = 0.1 + 0.2;
  if (std::abs(a - 0.3) < 1e-9) { /* 算它俩相等 */ }

2.4 布尔类型 bool

bool ok = true;    // 只有两个值:true / false
bool no = false;

打印 bool 会显示 1 或 0,想显示 true/false 需要设置格式:

std::println("{} {}", true, false);            // 1 0
std::println("{:b} {:b}", true, false);        // true false

2.5 字符 char 与字符串 string

char c = 'A';                  // 单引号 = 一个字符
std::string s = "你好";         // 双引号 = 字符串(多字符)
// 注意:std::string 需要 #include <string>

char 本质是小整数('A' 就是 65),所以可以参与算术:

char b = c + 1;   // 'B'

字符串的常用操作后面有专门一章(第六章),这里先认识它。

2.6 auto:让编译器猜类型(现代 C++ 最爱)

auto x = 42;        // 编译器自动推断:x 是 int
auto y = 3.14;      // y 是 double
auto s = std::string("hi");  // s 是 string

好处:

  1. 类型名太啰嗦时(后面会遇到很长的类型),auto 让代码清爽
  2. 以后类型变了,代码不用改
  3. 强迫你"先给初值再用",反而更安全

坏处(唯一一个):你自己看不出类型是什么。 需要确认真实类型时,用 static_assert 验证:

auto x = 42;
static_assert(std::is_same_v<decltype(x), int>); // 编译期检查

(这行看不懂没关系,第七章后自然懂,此处只是展示"可验证")

2.7 常量:不让变的量

const int MAX_USERS = 100;   // const:运行时不可改
constexpr int MAX = 100;     // constexpr:编译期就定死

区别(人话版):

比如数组大小。性能好,因为运行时根本没有这个变量。

经验法则:能 constexpr 就 constexpr,否则用 const。 宏 #define 是老 C 风格,现代 C++ 禁止用它定义常量。

一个编译期计算的例子:

constexpr int square(int n) { return n * n; }
constexpr int r = square(5);   // 编译时就算出 25

2.8 类型转换:类型之间怎么倒腾

1) 隐式转换(编译器自动做,但可能有坑):

   int x = 3.9;   // 小数截断成 3,没有警告(新版 clang 会警告)
   double y = 3;  // 整数升成 3.0,安全

2) 强制转换(你明确要求,C++ 老风格,不推荐):

   int x = (int)3.9;    // 3

3) 现代转换(推荐):

   int x = static_cast<int>(3.9);   // 3,静态转换
   // static_cast 以外的 cast 后面指针章节会提

一句话:小数转整数 = 直接丢掉小数部分(不是四舍五入)。

2.9 初始化:好几种写法,选一个固定用

int a = 5;        // 老式
int b(5);         // 括号式
int c{5};         // 花括号式(推荐,C++11 起)
int d{};          // 花括号空 = 初始化为 0

花括号的好处:不允许"缩窄"转换,比如 int x{3.9} 编译报错, 这能拦住大量隐性 bug。习惯用 {} 初始化。

2.10 引用:变量的第二个名字

int n = 10;
int& ref = n;      // ref 是 n 的别名,不是拷贝!
ref = 20;          // 改 ref 就是改 n
std::println("{}", n);   // 20

& 在类型里 = "引用"。引用必须初始化,且绑定后不能换人。 引用是后面学函数参数的关键,现在先混个脸熟。

本章小结

练习题


  1. 定义三个变量:int、double、string,打印出来。
  2. 算 int 最大值 + 1 的结果,亲眼看看溢出。
  3. 写一个 constexpr 函数算圆的面积(半径传入),打印结果。
  4. 用引用把两个变量交换值,验证原变量真的变了。