Skip to content

C++ 变量类型

变量是编程中的一个基本概念,代表内存中一个命名的区域,用于存储值。我们的程序使用变量在执行期间存储和操作数据。在 C++ 中,每个变量都必须有一个特定的数据类型,它决定了变量的大小、可以存储的值的种类以及适用于它的操作。

C++ 中的变量名(标识符)可以由字母、数字和下划线 (_) 组成。它们必须以字母或下划线开头。C++ 区分大小写,意味着 myVariable 和 myvariable 被视为不同的变量。

如前所述,C++ 有几种基本的内置类型:

除此之外,C++ 还允许定义更复杂的类型,如枚举 (enum, enum class)、指针、数组、引用、结构体 (struct)、联合体 (union) 和类 (class)。

定义将变量的名称和类型介绍给编译器,并且(关键的是)为其分配内存。定义也起到了声明的作用。

语法:

DataType variableName1, variableName2, /* ..., */ variableNameN;

在这里,DataType 是一个有效的 C++ 类型(例如 int、double、std::string 或用户自定义类型),而 variableName1 等是你选择的标识符。示例:

#include <string>
int score; # 定义一个名为 score 的整型变量
double temperature; # 定义一个双精度浮点变量
int x, y, z; # 定义三个整型变量
std::string message; # 定义一个字符串对象(需要包含 <string> 头文件)
bool isFinished; # 定义一个布尔变量

你可以在定义变量时为其赋予初始值(而且常常应该这样做)。这称为初始化。

C++ 提供了几种初始化语法:

  • 复制初始化 (=): 使用等号。历史悠久,很常见。
  • 直接初始化 (()): 使用括号。常用于调用构造函数。
  • 统一初始化 / 花括号初始化 ({}) (C++11 及以后版本): 使用花括号。通常是最推荐的形式,因为它无歧义 (unambiguous) 并防止窄化转换 (narrowing conversions)(例如,尝试用一个会丢失精度的 double 值初始化 int)。

示例:

#include <string>
int score = 0; # 复制初始化
double temperature = 20.5; # 复制初始化
int x(10); # 直接初始化
std::string name("Alice"); # 直接初始化(调用字符串构造函数)
// 花括号初始化(现代 C++ - 首选)
int count {0}; # 将 count 初始化为 0
double pi {3.14159};
std::string greeting {"Hello"};
int value {}; # 值初始化:对于基本类型初始化为 0
bool enabled {true};
// 花括号初始化可防止窄化转换:
// int narrowing_error {3.14}; # 编译时错误!无法将 double 窄化为 int。

重要提示: 在函数内部定义但未显式初始化的变量(局部变量)具有不确定值(垃圾值)。在使用局部变量之前务必对其进行初始化。在函数外部定义(全局变量)或定义为 static 的变量通常默认进行零初始化,但显式初始化仍然是良好的实践。

声明将变量的名称和类型介绍给编译器,但不分配内存。它本质上告诉编译器:“这个变量存在于别处,相信我。” 声明主要用于代码分布在多个文件中的情况。

使用 extern 关键字来声明变量而不定义它:

extern int globalCounter; # 声明:globalCounter 在别处定义
extern const double PI; # 声明:PI 是一个在别处定义的常量
extern std::string systemName; # 声明:systemName 在别处定义

一个变量可以被声明多次(例如,在不同的头文件中),但必须在某个源文件中精确地定义一次。定义是实际分配存储空间的地方。

globals.h(头文件):

#ifndef GLOBALS_H
#define GLOBALS_H
#include <string>
// 全局变量的声明
extern int sharedCounter;
extern const std::string appName;
#endif // GLOBALS_H

globals.cpp(源文件):

#include "globals.h"
// 全局变量的定义(在此分配内存)
int sharedCounter = 0; // 初始化的定义
const std::string appName = "My Awesome App";

main.cpp(另一个源文件):

#include <iostream>
#include "globals.h" // 包含声明
void incrementCounter(); // 假设此函数在别处定义
int main() {
std::cout << "Application: " << appName << std::endl; // 使用声明的全局常量
std::cout << "Initial counter: " << sharedCounter << std::endl; // 使用声明的全局变量
sharedCounter++;
incrementCounter(); // 另一个函数也可能修改它
std::cout << "Final counter: " << sharedCounter << std::endl;
return 0;
}

在此设置中,main.cpp 通过 globals.h 中的声明了解 sharedCounter 和 appName,但实际的内存和初始值是在 globals.cpp 中确定的。链接器 (linker) 将 main.cpp 中的使用与 globals.cpp 中的定义连接起来。

C++ 中的表达式可以根据它们是表示内存位置还是仅仅表示数据值进行分类。两个基本类别是:

  • 左值 (lvalue,locator value): 指代一个内存位置(一个对象)的表达式,该内存位置可能在单个表达式结束后仍然存在。变量通常是左值。左值通常可以出现在赋值运算符 (=) 的左侧。可以将其视为具有地址的东西。
  • 右值 (rvalue,read value): 指代一个临时数据值的表达式,该值不一定具有持久的内存位置(或者其位置不易获取或不可依赖)。字面量 (Literals)(例如 10、3.14、"hello")是右值。右值通常可以出现在赋值的右侧,但通常不能出现在左侧。

示例:

int x = 10; # 'x' 是一个左值,'10' 是一个右值
int y = 20; # 'y' 是一个左值,'20' 是一个右值
x = y; # OK: 'x' (左值) = 'y' (左值 - 在此隐式转换为右值)
x = 5; # OK: 'x' (左值) = '5' (右值)
# 以下无效,因为左侧不是左值:
// 10 = x; # 错误!不能赋值给字面量(右值)。
// (x + y) = 30; # 错误!(x + y) 产生一个临时结果(右值)。

理解左值和右值对于掌握 C++ 的高级特性(如引用 (references)、移动语义 (move semantics, C++11) 和运算符重载 (operator overloading))变得更加重要。