变量是C++程序中最基础也最重要的组成部分之一。简单来说,变量就是给计算机内存中的某个位置起的名字,我们可以通过这个名字来存储和访问数据。想象一下变量就像是一个个贴了标签的储物柜,每个柜子可以存放不同类型的物品(数据),而标签就是变量名。
在C++中,每个变量都有三个关键属性:
变量必须先声明后使用,声明的基本语法是:
cpp复制数据类型 变量名;
例如声明一个整型变量:
cpp复制int age;
注意:C++是强类型语言,这意味着变量的数据类型在编译时就已确定,不能随意改变。这与Python等动态类型语言有本质区别。
C++变量命名必须遵守以下规则:
有效变量名示例:
cpp复制int _count;
double averageScore;
string user_name123;
无效变量名示例:
cpp复制int 123var; // 数字开头
float user-name; // 包含连字符
char class; // 使用关键字
虽然语法上只要符合规则就可以,但良好的命名习惯能显著提高代码可读性:
使用有意义的名称:避免单字母命名(除非是循环计数器等简单场景)
遵循命名约定:
避免混淆:
个人经验:我习惯对局部变量使用小驼峰,对类成员变量加m_前缀(如m_count),对常量使用全大写加下划线(如MAX_SIZE)。这种一致性让代码更易维护。
C++中变量声明有以下几种形式:
cpp复制int count; // 声明但未初始化
cpp复制double price = 99.99; // C风格初始化
float balance {100.0f}; // C++11列表初始化
cpp复制int x = 1, y = 2, z = 3; // 同时声明多个变量
注意:未初始化的变量包含的是该内存位置的随机值,直接使用可能导致不可预测的行为。这是一个常见的初学者错误。
C++11引入了更安全的初始化语法:
cpp复制int age{25}; // 如果类型不匹配会报错
double salary = {5000.0};
cpp复制vector<int> numbers {1, 2, 3};
列表初始化的优点:
cpp复制int total{}; // 初始化为0
double scores[10]{}; // 数组全部元素初始化为0.0
cpp复制auto name = "Alice"; // 自动推断为const char*
auto score = 95.5; // 自动推断为double
cpp复制auto [id, name] = getUser(); // 分解返回的元组
C++提供了多种整数类型以适应不同需求:
| 类型 | 大小(字节) | 范围 | 说明 |
|---|---|---|---|
| short | 2 | -32,768~32,767 | 短整型 |
| int | 4 | -2^31~2^31-1 | 最常用的整型 |
| long | 4或8 | 平台相关 | 长整型 |
| long long | 8 | -2^63~2^63-1 | 超长整型(C++11) |
| unsigned X | 同X | 0~2^n-1 | 无符号版本 |
示例:
cpp复制short daysInWeek = 7;
int population = 1000000;
long long bigNumber = 9'223'372'036'854'775'807; // C++14数字分隔符
注意:不同平台下类型大小可能不同,如果需要确定大小,可以使用
中的固定宽度整数类型(如int32_t)。
浮点类型用于表示实数:
| 类型 | 大小(字节) | 精度 | 范围 |
|---|---|---|---|
| float | 4 | 6-7位 | ±1.18×10^-38~±3.4×10^38 |
| double | 8 | 15-16位 | ±2.23×10^-308~±1.80×10^308 |
| long double | 8或16 | 平台相关 | 更高精度 |
示例:
cpp复制float pi = 3.14159f; // 注意f后缀
double atomicMass = 1.66053906660e-27; // 科学计数法
浮点数比较技巧:
cpp复制// 不要直接比较浮点数
if (abs(a - b) < 1e-9) { /* 认为相等 */ }
cpp复制char ch = 'A'; // 通常1字节
wchar_t wideCh = L'你'; // 宽字符
char16_t utf16Ch = u'字'; // C++11
char32_t utf32Ch = U'🍎'; // C++11
cpp复制bool isReady = true;
bool isEmpty = false;
注意:C++中非零值转换为true,零值转换为false。但最好显式使用true/false以提高可读性。
cpp复制void func() {
int x = 10; // 只在func内可见
{
int y = 20; // 只在这个块内可见
}
// y在这里不可访问
}
cpp复制int globalVar; // 全局变量
int main() {
globalVar = 100; // 可以访问
}
cpp复制class MyClass {
int memberVar; // 类作用域
};
cpp复制namespace MyNS {
int nsVar; // 命名空间作用域
}
cpp复制void func() {
auto int x; // auto可省略(C++11前)
// 函数结束时销毁
}
cpp复制void counter() {
static int count = 0; // 只初始化一次
count++;
}
cpp复制int* p = new int(10); // 手动管理生命周期
delete p; // 必须手动释放
经验之谈:尽量减少全局变量的使用,优先使用局部变量。必须使用全局变量时,考虑用命名空间封装。
const表示变量值不可修改:
cpp复制const double PI = 3.1415926;
// PI = 3.14; // 错误:不能修改const变量
const指针的几种形式:
cpp复制const int* p1; // 指向常量的指针
int* const p2; // 指针本身是常量
const int* const p3; // 指向常量的常量指针
编译时常量:
cpp复制constexpr int SIZE = 100;
constexpr double SQUARE_ROOT_2 = 1.41421;
constexpr函数:
cpp复制constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n-1);
}
告诉编译器变量可能被意外修改:
cpp复制volatile bool interruptFlag = false;
// 常用于硬件寄存器访问
允许在const成员函数中修改:
cpp复制class Cache {
mutable bool dirty; // 可以在const方法中修改
public:
void update() const { dirty = true; }
};
自动发生的类型转换:
cpp复制int i = 3.14; // double→int,丢失小数部分
double d = i; // int→double,安全转换
cpp复制double d = (double)i; // 不推荐
cpp复制// 静态转换(编译时检查)
double d = static_cast<double>(i);
// 重新解释转换(危险)
int* p = reinterpret_cast<int*>(0x1234);
// 常量转换
const int* cp = &i;
int* p = const_cast<int*>(cp);
// 动态转换(运行时检查,用于多态)
Derived* d = dynamic_cast<Derived*>(base);
cpp复制typedef unsigned long ulong;
cpp复制using ulong = unsigned long;
cpp复制template<typename T>
using Vec = std::vector<T>;
cpp复制int x;
cout << x; // 未定义行为
cpp复制{
int y = 10;
}
cout << y; // 错误:y不在作用域内
cpp复制int n = "hello"; // 错误:字符串不能赋给int
cpp复制int count;
double count; // 错误:重复定义
cpp复制// gdb/lldb命令:
print x // 查看变量值
watch x // 监视变量变化
cpp复制#define DEBUG(x) cout << #x << " = " << x << endl
int value = 42;
DEBUG(value); // 输出:value = 42
cpp复制// 不好
string s1 = getString();
string s2 = s1; // 拷贝
// 更好(C++11)
string s2 = std::move(s1); // 移动语义
cpp复制// 连续内存访问更快
int array[100][100];
// 按行访问
for (int i = 0; i < 100; ++i)
for (int j = 0; j < 100; ++j)
array[i][j] = 0;
cpp复制register int counter = 0; // 已弃用
// 现代编译器会自动优化
在实际项目中,我发现变量命名不当和类型混淆是最常见的两类问题。建议在团队中制定统一的命名规范,并使用静态分析工具在编译期捕获类型相关问题。对于性能敏感的场景,要特别注意变量的存储位置(栈/堆/寄存器)和访问模式。