变量提供一个具名的、可供程序操作的存储空间。一般而言,"变量(variable)"和“对象(object)"可以互换使用。
2.2.1 变量定义
变量定义基本形式:首先是类型说明符(type specifier),随后紧跟由一个或多个变量名组成的列表,变量名以逗号分隔,最后以分号结束。
初始值
对象在创建时获得了一个特定的值,称为对象被初始化(initialized)。用于初始化变量的值可以是任意复杂的表达式。
在同一条定义语句中,可以用先定义的变量值去初始化后定义的其他变量。
初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,赋值的含义是把对象当前的值擦除,以一个新值来替代。
列表初始化
初始化有几种不同的形式,例如定义一个名为 units_sold 的 int 变量并初始化为 0,以下语句均可实现:
int units_sold = 0;
int units_sold = {
0};
int units_sold{
0};
int units_sold(0);
利用花括号进行初始化的形式被称为列表初始化(list initialization),初始化对象或某些情况下为对象赋新值,均可使用列表初始化。
使用列表初始化且初始值存在信息的风险,编译器将报错,如:
long double ld = 3.1415926536;
int a{
ld}, b = {
ld}; // 错误: 转换未执行,因为存在丢失信息的危险
int a(ld), b = ld; // 正确: 转换执行,且确实丢失了部分值
默认初始化
如果定义变量时没有定义初始值,则变量被默认初始化(default initialized)。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。
如果是内置类型的变量未被显式初始化,它的值由定义的位置决定:
- 定义于任何函数体之外的变量被初始化为 0 。
- 定义在函数体内部的内置类型变量将不被初始化,变量的值时未定义的
每个类各自决定其初始化对象的方式,而且,是否允许不经初始化就定义对象也由类自己决定。
std::cin >> int input_value; // 错误:输入运算符的右侧需要一个明确的变量名称
int i = {
3.14}; // 告警
double salary = wage = 9999.99; // 错误:在声明语句中声明多个变量时需要用逗号将变量名隔开,不能直接用赋值运算符连接
int i = 3.14; // 告警
2.2.2 变量声明和定义的关系
为了支持分离式编译,C++ 语言将声明和定义区分开来。
声明(declaration)使得名字为程序所知,定义(definition)负责创建与名字关联的实体。
变量声明规定了变量的类型和名字,定义则还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern
,而且不要显式地初始化变量。
任何包含了显式初始化的声明即成为定义,extern
语句如果包含初始值就不再是声明,而变成了定义。
变量的定义必须出现且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
2.2.3 标识符(identifier)
标识符由字符、数字和下划线组成,其中必须已字母或下划线开头。用户自定义的标识符不能使用 C++ 保留的名字,不能连续出现两个下划线,也不能已下划线紧连大写字母开头,定义在函数体外的标识符不能以下划线开头。
变量命名规范
- 标识符要能体现实际含义
- 变量名一般用小写字母
- 用户自定义的类名一般以大写字母开头
- 如果标识符由多个单词组成,则单词间要有明显区分,如 student_loan 或 studentLoan
2.2.4 名字的作用域(scope)
绝大多数作用域都以花括号分隔。同一个名字在不同的作用域可能指向不同的实体。名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束。
嵌套作用域
作用域能彼此包含,被包含(被嵌套)的作用域称为内层作用域(inner scope),包含着别的作用域的作用域称为外层作用域(outer scope)。
作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字。同时,允许在内层作用域中重新定义外层作用域已有的名字。