C++变量
变量其实只不过是程序可操作的存储区的名称。
C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。
一.C++中的变量定义
变量定义是为变量分配存储空间的动作。
- 作用: 它告诉编译器:“我要创建一个名为
X、类型为Y的变量,请在内存中为它留出空间。” - 特性:
- 唯一性: 每个变量在整个程序中只能被定义一次。
- 内存分配: 定义会伴随着内存的分配。
- 初始化: 定义时可以同时对变量进行初始化(赋值)。
示例:int a; // 定义:为 int 类型的变量 a 分配内存,并默认初始化
double pi = 3.14159; // 定义:为 double 类型的变量 pi 分配内存,并初始化为 3.14159
二.C++ 中的变量声明
变量声明是向编译器告知变量的类型和名称,但不分配内存空间。
- 作用: 它的主要目的是让编译器知道这个变量是存在的,以及它是什么类型的,以便在当前文件中使用它,即使它的实际存储空间是在另一个文件中定义的。
- 关键字: 在函数体外声明其他文件定义的变量时,通常使用
extern关键字。
示例:
假设你有一个名为 data.cpp 的文件,其中定义了 count:// data.cpp
int count = 10; // 这是定义,分配了内存
如果你想在另一个文件 main.cpp 中使用 count,你需要先声明它:// main.cpp
extern int count; // 这是声明,它告诉编译器:
// “存在一个 int 类型的变量 count,但它在别处定义和分配了内存,
// 我在这里要引用它。”
void use_count() {
count++; // 使用 count
}
特殊情况:没有 extern 关键字的声明
在 C++ 中,一个未被初始化的普通声明如果在全局(函数体外)或命名空间范围内,它同时也是一个定义:int value; // 既是声明,也是定义(在全局或命名空间范围)
只有在以下情况中,声明才不是定义:
- 使用了
extern关键字,且没有显式初始化:extern int i; // 声明,不是定义
- 函数声明(函数声明也属于声明,不分配函数体内存):
int max(int a, int b); // 声明,不是定义
三.C++中的变量初始化
在 C++ 中,定义变量时如果没有显式初始化,变量的值取决于它的存储类型和作用域,可能出现 “未定义值”“默认初始化” 或 “零初始化” 等情况,具体规则如下:
1. 局部变量(自动存储期,auto):值是 “未定义的(undefined)”
- 场景:在函数内部、代码块(
{})中定义的非静态局部变量(默认是auto存储类型,可省略auto关键字)。 - 行为:未初始化时,变量的值是不确定的(垃圾值),它会随机占用内存中该位置之前遗留的二进制数据。
- 风险:使用这类变量的值会导致未定义行为,可能引发程序崩溃、结果错误等不可预测的问题。
void func() { |
2. 静态变量(static)和全局变量:默认 “零初始化”
- 场景:
- 全局变量(定义在所有函数 / 类外部);
static修饰的局部变量(函数内的静态变量);static修饰的类成员变量(静态成员)。
- 行为:如果未显式初始化,编译器会自动将其初始化为零值(zero-initialized),具体规则:
- 数值类型(
int、double等)→0或0.0; - 指针类型 →
nullptr; - 布尔类型 →
false; - 数组 / 结构体 → 每个成员都按上述规则零初始化。
- 数值类型(
int g; // 全局变量,未初始化→默认0 |
3. 类 / 结构体的成员变量:默认初始化依赖构造函数
- 场景:类或结构体的非静态成员变量。
- 行为:
- 如果类没有自定义构造函数,且成员变量未在初始化列表或声明时初始化,则其值不确定(类似局部变量);
- 如果类有自定义构造函数,但未在构造函数中初始化成员变量,则值不确定;
- 若使用值初始化(如
MyClass obj{};),即使没有显式初始化,成员变量也会被零初始化(C++11 及以上)。
struct Test { |
四.全局变量
1. 定义与作用域
- 定义位置: 定义在所有函数体(包括
main函数)外部的变量。 - 作用域: 具有文件作用域。一旦定义,从定义点开始,直到文件末尾,该变量都有效。
- 多文件注意: 如果需要在其他文件中使用同一个全局变量,需要使用
extern关键字进行声明。
2. 生命周期
- 持续时间: 具有静态生命周期。程序启动时创建,程序结束时销毁。
- 初始化: 如果没有显式初始化,全局变量会被默认初始化为零值(例如,
int初始化为0,指针初始化为nullptr)。
3. 存储位置
- 全局变量通常存储在程序的静态/全局数据区中。
4. 示例
|
五.局部变量
1. 定义与作用域
- 定义位置: 定义在函数体内部、代码块
{}内部、或函数参数列表中的变量。 - 作用域: 具有块作用域。它们只能在定义它们的函数或代码块内部访问。一旦离开了定义它的
{},变量就变得不可访问。
2. 生命周期
- 持续时间: 具有自动生命周期。当程序执行流进入定义它们的代码块时创建,当程序执行流离开该代码块时销毁。
- 初始化: 如果没有显式初始化(也叫未初始化),局部变量将包含不确定的值(通常是内存中已有的垃圾数据)。这是一个常见的错误来源!
3. 存储位置
- 局部变量通常存储在程序的栈区中。
4. 示例
|
六.静态变量
无论 static 关键字用在哪里,它首先改变的是变量的生命周期:
静态生命周期 (Static Lifetime): 变量从程序启动时创建,到程序结束时销毁。
这意味着,即使在函数内部,静态变量也只初始化一次,并且在函数调用结束后,它的值会保留下来。
一、静态局部变量
当在函数内部声明一个局部变量时,使用 static 关键字。
1. 作用与特性
- 作用域: 保持局部作用域。它只能在定义它的函数内部被访问(和普通局部变量一样)。
- 生命周期: 变为静态生命周期。
- 只初始化一次:即使函数被多次调用,初始化语句也只在第一次进入函数时执行。
- 值保持:函数执行结束后,变量的值会保留在内存中,直到程序结束。
2. 示例
void counter() {
// 普通局部变量:每次函数调用时创建,并初始化为 0
int non_static_count = 0;
// 静态局部变量:只在程序启动时或第一次调用时初始化为 0
static int static_count = 0;
non_static_count++;
static_count++;
std::cout << "非静态计数: " << non_static_count
<< ", 静态计数: " << static_count << std::endl;
}
int main() {
std::cout << "第一次调用: ";
counter(); // non_static_count: 1, static_count: 1
std::cout << "第二次调用: ";
counter(); // non_static_count: 1 (重新初始化), static_count: 2 (值保留)
std::cout << "第三次调用: ";
counter(); // non_static_count: 1 (重新初始化), static_count: 3 (值保留)
return 0;
}
用途: 常用于实现一个函数内共享的、需要保持状态的计数器,或用于存储只需要进行一次的昂贵计算结果。
二、静态全局变量
当在文件(源文件 .cpp)的全局作用域内声明变量时,使用 static 关键字。
注意: 在现代 C++ 中,不推荐使用
static来限制全局变量的作用域,而应使用匿名命名空间来实现同样的目的。
1. 作用与特性
- 作用域限制(文件作用域):
static在这里的作用是限制变量的可见性。它将全局变量的作用域限制在定义它的当前源文件(编译单元)内。 - 链接属性: 具有内部链接。这意味着它不能被其他源文件通过
extern关键字访问或引用。
2. 示例
假设你有 file1.cpp 和 file2.cpp。
在 file1.cpp 中:// 静态全局变量:只能在 file1.cpp 中使用
static int private_data = 5;
// 普通全局变量:可以在其他文件用 extern 引用
int public_data = 10;
在 file2.cpp 中:
// 可以引用 public_data
extern int public_data;
// 尝试引用 private_data 会在链接阶段失败!
// extern int private_data;
void access_data() {
std::cout << "Public Data: " << public_data << std::endl; // 10
// std::cout << "Private Data: " << private_data << std::endl; // 编译/链接错误
}
用途: 创建仅供当前源文件内部使用的全局配置或数据,避免命名冲突。
三、静态成员变量
当在 C++ 类内部声明一个成员变量时,使用 static 关键字。
1. 作用与特性
- 独有性: 静态成员变量不属于任何特定的对象。它是该类的所有对象共享的一个变量。
- 内存分配: 即使没有创建任何对象,静态成员也存在,并且只分配一次内存。
- 初始化: 必须在类定义外部进行定义和初始化(通常在
.cpp文件中)。
2. 示例
|
用途: 常用于跟踪类实例的数量、存储所有对象共享的配置数据、或实现单例模式。