3. C++语言基础:标识符、关键字、基本数据类型、常量与变量、类型转换

C++作为一门静态类型语言,其一切程序逻辑都建立在“数据如何命名、如何存储、如何变换”的基石之上。本章将系统梳理C++的标识符与关键字规则、五种基本数据类型的存储特性、常量与变量的本质区别,以及类型转换的各类场景与风险。掌握这些基础,是后续理解运算符重载、函数传参、类与对象的前提。下图展示了本章知识点的整体结构关系:

C++语言基础 标识符与关键字 命名规则 · 保留字 · 命名规范 基本数据类型 int · float · double · char · bool 常量与变量 const · 字面量 · 存储与生命周期 类型转换 隐式转换 · static_cast · 精度损失 核心要点 类型决定存储大小与解释方式 · 转换需关注精度与范围

3.1 标识符与关键字

3.1.1 核心原理:标识符的构成规则

标识符是程序员为变量、函数、类、常量等实体起的名字。C++标准规定,标识符只能由字母(a-z、A-Z)、数字(0-9)和下划线(_)组成,且不能以数字开头。同时,标识符区分大小写,即 count 与 Count 是两个不同的标识符。C++语言本身对标识符长度没有硬性限制,但不同编译器可能有实现层面的上限(通常至少支持 1024 个字符)。

关键字(又称保留字)是C++标准预先定义好的、具有特殊含义的单词,程序员不能将其用作标识符。例如 int、if、while、class、new、delete 等。C++11 及后续标准还新增了 nullptr、override、final 等关键字。此外,还有一些替代标记(如 and、or、not)也属于保留字范畴。

3.1.2 判断方法:如何检查标识符合法性

在实际编码中,判断一个标识符是否合法,可以按以下三步快速检查:

  1. 字符集检查:逐个字符确认是否属于字母、数字或下划线,出现其他字符(如 @、#、空格)即非法。
  2. 首字符检查:首字符不能是数字。例如 2value 非法,value2 合法。
  3. 关键字比对:确认该名称不在C++关键字列表中。例如 class 非法,class_name 合法。
示例标识符是否合法原因说明
student_name✅ 合法字母+下划线,符合规则
_tempValue✅ 合法下划线开头允许(但全局命名空间建议避免)
2ndScore❌ 非法以数字开头
my-name❌ 非法包含连字符 -
float❌ 非法属于C++关键字
int_value✅ 合法虽然包含 int 子串,但整体不是关键字

3.1.3 实战场景:命名规范与可读性

在二级考试和实际项目中,命名规范直接影响代码质量。推荐采用以下约定:

  • 变量名:使用小写字母开头,后续单词首字母大写(驼峰式),如 studentAge;或全部小写加下划线,如 student_age。
  • 常量名:全大写字母加下划线,如 MAX_SIZE。
  • 函数名:与变量命名类似,通常使用动词开头,如 getTotalScore()。
  • 类名:每个单词首字母大写,如 StudentInfo。
经验提醒:在C++中,以下划线开头的标识符(如 _name)虽然在局部作用域合法,但在全局作用域可能与编译器内部名称冲突,建议避免。另外,双下划线(__)开头的标识符在任何情况下都是保留的,切勿使用。

3.1.4 常见误区与总结

初学者常犯的错误包括:混淆大小写(Score 与 score 不同)、使用中文或特殊符号命名(部分编译器支持但不符合标准)、将关键字误作变量名(如 int new = 5; 编译报错)。总结:标识符命名应做到“合法、清晰、一致”,这是程序可维护性的第一道防线。

3.2 基本数据类型

3.2.1 核心原理:五种基本类型的存储与范围

C++的基本数据类型决定了数据在内存中占用的字节数以及可表示的数值范围。以下是标准规定(在大多数32位/64位平台上的典型值):

类型字节数取值范围典型用途
int4-2,147,483,648 ~ 2,147,483,647整数计数、索引、状态码
float4约 ±3.4×1038(7位有效精度)科学计算、图形坐标
double8约 ±1.7×10308(15-16位有效精度)高精度计算、金融计算
char1-128 ~ 127 或 0 ~ 255(取决于是否signed)字符存储、字节操作
bool1true 或 false逻辑判断、标志位

需要特别注意的是,int 的字节数并非C++标准强制规定为4字节,而是“至少2字节”,但在现代桌面和服务器平台上几乎都是4字节。考试中如无特殊说明,按4字节处理。char 类型既可以存储字符(如 'A'),也可以存储小范围整数(如 65),因为字符在内存中本质上是ASCII码值。

3.2.2 判断方法:如何选择合适的数据类型

选择数据类型时,可参考以下决策流程:

  1. 是否需要小数? 不需要 → 整数类型(int 优先);需要 → 浮点类型(double 优先,除非内存极度受限才用 float)。
  2. 数值范围是否可能超过 int 上限? 是 → 考虑 long long(8字节)或 double(如果允许精度损失)。
  3. 是否需要存储单个字符? 是 → 使用 char。
  4. 是否需要布尔逻辑? 是 → 使用 bool,不要用 int 代替。

3.2.3 实战场景:类型选择不当引发的Bug

某学生编写银行账户余额计算程序,初始代码如下:

float balance = 1000000.0f;   // 100万元
float interest = balance * 0.035f;  // 年利率3.5%
cout << "利息为:" << interest << endl;

运行结果输出“利息为:35000”,看似正确。但当余额变为 123456789.0f 时,由于 float 只有7位有效数字,实际存储值可能变为 123456792.0,导致利息计算出现约几元的误差。改用 double 后,精度提升到15-16位有效数字,该问题得到解决。

风险提示:浮点数(float/double)在计算机中采用二进制科学计数法存储,无法精确表示所有十进制小数(如 0.1 在二进制中是无限循环小数)。因此,涉及金额、精确比较等场景,应使用整数(以“分”为单位)或专门的高精度库,而非直接使用浮点数。

3.2.4 常见误区与总结

常见误区包括:混淆 float 与 double 的精度差异;误以为 char 只能存字符;用 int 存储布尔值(虽然合法但不推荐)。总结:数据类型的选择是“范围、精度、内存”三者权衡的结果,默认优先使用 int 和 double。

3.3 常量与变量

3.3.1 核心原理:定义与本质区别

变量是程序运行过程中值可以改变的量,定义时系统为其分配内存空间,通过变量名访问该空间。常量是值在程序运行期间不可改变的量。C++中定义常量有两种主要方式:

  • 字面量常量:直接写在代码中的值,如 42、3.14、'A'、"hello"。
  • const 限定常量:使用 const 关键字修饰的变量,如 const int MAX = 100;。

两者的本质区别在于:const 常量具有明确的类型,编译时会进行类型检查,且作用域规则与变量一致;而字面量常量没有独立的内存地址(除非取地址操作),其类型由编译器根据字面形式推断(如 3.14 默认为 double,100 默认为 int)。

3.3.2 操作流程:变量的声明与初始化

C++中变量使用前必须先声明,推荐在声明时立即初始化:

int score = 0;              // 声明并初始化为0
double pi = 3.14159;        // 浮点变量
char grade = 'A';           // 字符变量
bool isPassed = true;       // 布尔变量

// 常量定义
const int STUDENT_NUM = 50;
const double TAX_RATE = 0.06;

注意:C++11 起支持列表初始化 int x{0};,这种方式在类型转换可能丢失数据时会报编译错误,更安全。

3.3.3 实战场景:const 的编译期优化

在考试和实际项目中,const 常量在编译时会被直接替换为字面值(宏展开的替代品),因此不会占用额外内存(取决于编译器优化)。例如:

const int ARRAY_SIZE = 10;
int arr[ARRAY_SIZE];  // 编译时 ARRAY_SIZE 被替换为 10

这比使用 #define ARRAY_SIZE 10 更安全,因为 const 有类型检查,而宏定义只是简单的文本替换,不进行类型检查。

关键对比:const 常量 vs #define 宏定义
• const 有类型,#define 无类型
• const 受作用域限制,#define 从定义处到文件末尾全局生效
• const 可以用于类成员,#define 不能
• 推荐优先使用 const

3.3.4 常见误区与总结

常见误区:忘记初始化变量(局部变量默认值是随机的);试图修改 const 常量(编译报错);混淆 const 与 constexpr(后者要求编译期即可计算出值)。总结:变量是“可变的存储”,常量是“不可变的约定”,合理使用 const 能提高代码的安全性和可读性。

3.4 类型转换

3.4.1 核心原理:隐式转换与显式转换

类型转换分为两类:隐式转换(自动发生,无需程序员干预)和显式转换(程序员通过语法强制指定)。隐式转换遵循“提升”原则:bool → char → int → unsigned int → long → long long → float → double → long double。例如:

int a = 5;
double b = a;      // int 隐式转换为 double,安全
double c = 3.7;
int d = c;         // double 隐式转换为 int,小数部分被截断,d = 3

显式转换在C++中推荐使用 static_cast,而非C风格的 (type)value:

double price = 19.99;
int total = static_cast<int>(price) * 3;  // 先截断为19,再乘3得57

3.4.2 判断方法:何时需要显式转换

以下情况必须使用显式转换:

  1. 窄化转换:从 double 到 int、从 long 到 int,可能丢失数据。
  2. 类型不匹配的运算:如整数除法需要浮点结果时,需将操作数转为 double。
  3. 指针类型转换(本章不展开,后续章节涉及)。

3.4.3 实战场景:整数除法的陷阱

某程序需要计算平均分:

int totalScore = 450;
int studentCount = 7;
double average = totalScore / studentCount;  // 错误!整数除法结果为64,再转为double仍为64.0
// 正确写法:
double averageCorrect = static_cast<double>(totalScore) / studentCount;  // 结果为64.2857...

这个案例是二级考试的高频考点。整数除法会直接截断小数部分,即使赋值给 double 变量也无济于事,因为除法运算发生在赋值之前。

风险提示:隐式转换中的“截断”是静默发生的,编译器通常只给出警告(warning)而非错误(error)。在金融、控制等对精度敏感的系统中,务必使用显式转换并检查数值范围,避免因截断导致逻辑错误。

3.4.4 常见误区与总结

常见误区:认为 double → int 是四舍五入(实际是向零截断);混淆 static_cast 与 reinterpret_cast(后者用于指针/引用转换,风险极高);在比较运算中忽视类型提升(如 int 与 unsigned int 比较时,int 会被转为 unsigned int,负数会变成很大的正数)。总结:类型转换是C++中“最安静的错误来源”之一,显式转换 + 范围检查是防御性编程的基本功。

本章核心要点回顾:
1. 标识符:字母/数字/下划线组成,不能以数字开头,避开关键字。
2. 基本类型:int(4字节)、double(8字节,优先于float)、char(1字节)、bool(1字节)。
3. 常量用 const 定义,有类型检查,优于宏定义。
4. 类型转换:隐式转换遵循提升规则,窄化转换必须显式使用 static_cast。
5. 整数除法陷阱:先转换再运算,避免截断错误。