2.2 变量
2.2.1 变量定义
变量定义的基本形式是:首先是类型说明符,随后紧跟一个或多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。列表中每个变量名的类型都由类型说明符指定,定义时还可以为一个或多个变量赋初值:
int sum = 0, value; // sum 和 value 都是int,且sum的初始值为0
【初始值】
当对象在创建的时候获得了一个特定的值,则称这个对象被初始化了。用于初始化变量的值可以是任意复杂的表达式。当一次定义了两个或多个变量时,对象的名字随着定义也就马上可以使用了,即在同一条定义语句中,可以用先定义的变量去初始化后定义的其它变量:
// 先定义price,然后用定义好的price去初始化discount
double price = 0.9, discount = price * 0.8;
【列表初始化】
C++语言定义了初始化的好几种不同形式,例如想要定义一个名为 my_name 的 int 变量并初始化为0,可以用以下4条语句:
int my_name = 0;
int my_name = {0};
int my_name(0);
int my_name{0};
作为 C++11 新标准的一部分,用花括号来初始化变量的这种初始化形式被称为列表初始化,目前这种形式已经得到了全面应用,无论是初始化对象还是某些时候为对象赋新值。
【注意】当用于内置类型的变量时,列表初始化有一个重要特点:如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错。例如:
long double ld = 3.1415926536;
int a{ld}, b = {ld}; // 错误:转换未执行,因为存在丢失信息的危险
int c(ld), d = ld; // 正确:转换执行,且确实丢失了部分值
上面的例子中,使用 long double 的值初始化 int 变量时可能丢失数据,所以编译器拒绝了 a 和 b 的初始化请求。其中,至少 ld 的小数部分会丢失,且 int 也可能存不下 ld 的整数部分。
【默认初始化】
如果定义变量时没有指定初值,则变量被默认初始化,而默认值到底是什么,则由变量的类型及定义变量的位置决定。
如果是内置的变量未被显示初始化,它的值由定义它的位置决定。定义于任何函数体之外的变量被初始化为0,但有一种例外情况是,定义在函数体内部的内置类型变量将不被初始化(一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或以其它形式访问此类值时将发生错误)。
2.2.2 变量声明和定义的关系
为了允许程序拆成多个逻辑部分来写,C++支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可以被独立编译。
为了支持分离式编译,C++语言将声明和定义区分开:
(1)声明
使得名字为程序所知,一个文件如果想使用别处定义的名字,则必须包含对那个名字的声明。
(2)定义
负责创建与名字关联的尸体。
(3)声明与定义的区别
变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请了存储空间,也可能会为变量赋一个初始值。要想声明一个变量而非定义它,就在变量名前添加关键字 extern,且不要显示地初始化变量:
extern int i; // 声明i而非定义i
int j; // 声明并定义j
【注意】任何包含了显示初始化的声明即成为定义。我们可以给由extern关键字标记的变量赋一个初始值,但这么做也就抵消了extern的作用。extern语句如果包含了初始值就不再是声明,而变成了定义:
extern double pi = 3.141; // 定义
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。变量能且只能被定义一次,但可以被声明多次。
2.2.3 名字的作用域
作用域是程序的一部分,在其中名字有其特定的含义。C++中大多数作用域都以花括号分隔。
名字main定义于花括号之外,它和其它大多数定义在函数体之外的名字一样,拥有全局作用域。一旦声明以后,全局作用域内的名字在整个程序的范围内都可以使用。
【嵌套的作用域】
作用域能彼此包含,被包含的作用域称为内层作用域,包含着别的作用域的作用域称为外层作用域。
作用域中一旦声明了某个名字,它所嵌套的所有作用域中都能访问该名字,同时也允许在内层作用域中重新定义外层作用域已有的名字,例如:
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <map>
#include <queue>
#include <stack>
using namespace std;
int a = 10;
int main()
{
int b = 0;
cout << "Output 1 : " << a << " " << b << endl;
int a = 5; // 新建局部变量a,覆盖了全局变量a
cout << "Output 2 : " << a << " " << b << endl;
// 显示地访问全局变量a
/*
因为全局作用域本身并没有名字,所以当作用域操作符的左侧为空时,向全局作用域发出请 求获取作用域操作符右侧名字对应的变量
*/
cout << "Output 3 : " << ::a << " " << b << endl;
return 0;
}
该程序的运行结果为:
Output 1 : 10 0
Output 2 : 5 0
Output 3 : 10 0