假定要使用一个变量来记录我们拥有的苹果数。可以用变量的声明语句创建一个变量apples。如图2-2所示。

图2-2 变量的声明
图2-2中的这个语句称为声明,因为它声明了名称apples。把名称引入程序的语句称为该名称的声明。例子中的语句也称为定义,因为它为变量apples分配了内存空间。有的声明语句并不是定义。变量根据其定义来创建,所以只有在定义语句之后才能引用这个变量。如果试图在定义之前就引用变量,编译器就会发送一个错误消息。
在定义变量时,还可以指定它的初始值。例如:
int apples=10; //Definition for the variable apples
这个语句定义了变量apples,并把它的初始值设置为10。图2-2中的定义没有指定初始值,所以分配给该变量的内存包含上次使用该内存空间时遗留下来的垃圾值。让垃圾值留在程序中并不好,现在定义第一个黄金规则:
在定义变量时一定要指定其初始值。如果不知道该变量应包含什么值,可以把它初始化为0。
可以把变量用作前面介绍的算术运算符的操作数,其方式与使用字面量相同。变量的值就是操作数的值。如果对一个变量应用一元减号运算符,就会得到该变量值的负值,但数值是相同的。这不会改变变量中存储的值。稍后讨论它。
下面在一个小程序中使用整型变量。
程序示例2.2—— 使用整型变量
下面的程序说明了如何把苹果平均地分给一组小朋友:
//Program 2.2 – Working with integer variables
#include <iostream> //For output to the screen
using std:cout;
using std:endl;
int main() {
int apples=10; //Definition for the variable apples
int children=3; //Definition for the variable children
// Calculate fruit per child
cout << endl //Start on a new line
<< "Each child gets //Output some text
<< apples / children //Output number of apples per child
<< " fruit."; //Output some more text
// Calculate number left over
cout << endl //Start on a new line
<< " We have " //Output some text
<< apples % children //output apples left over
<< "left over."; //Output some more text
cout << endl;
return 0; //End the program
}
该程序带有非常详细的注释,说明了每个语句的执行情况。一般不需要在注释中放置如此一目了然的信息。这个程序的输出结果如下所示:
Each child gets 3 fruits.
We have 1 left over.
例子的说明
这个例子不会使读者的脑细胞负担过重。main()中的前两个语句定义了变量apples和children:
int apples=10; //Definition for the variable apples
int children=3; //Definition for the variable children
变量apples初始化为10,变量children初始化为3。还可以在一个语句中定义这两个变量。例如:
int apples=10, children=3;
此语句把apples和children都声明为int类型,并把它们分别初始化为10和3。使用逗号把要声明的变量分隔开,整个语句用一个分号结束。当然,在这里添加额外的注释并不是很容易,因为空间不够,但可以把该语句分开放在两行上:
int apples=10, //Definition for the variable apples
children=3; //Definition for the variable children
仍然用逗号把两个变量分隔开,现在就有足够的空间在每一行的末尾添加注释了。可以在一条语句中声明任意多个变量,也可以把语句分散放在任意多行代码上。但是,最好是每条语句只有一个声明。
下一条语句计算每个孩子可以得到几个苹果,并输出结果:
cout << endl // Start on a new line
<< "Each child gets " // Output some text
<< apples / children // Output number of apples per child
<<" fruit."; // Output some more text
注意这4行代码组成了一条语句,我们把注释放在语句中的每一行上。算术表达式使用除法运算符获得每个小朋友得到的苹果数。这个表达式只涉及刚才定义的两个变量,但一般情况下,可以在表达式中以任意方式混合变量和字面量。
下一条语句计算并输出剩余的苹果数:
cout << endl // Start on a new line
<< " We have " // Output some text
<< apples % children // Output apples left over
<< "left over."; // Output some more text
这里使用取模运算符计算余数,结果是输出语句中的文本字符串。还可以把所有的输出生成为一行语句。另外,也可以在单独语句中输出每个字符串和数据值。
在这个例子中,变量使用了int类型,还有其他整型变量。
2.4.1 整型变量类型
整型变量的类型将决定给它分配多少内存空间,最终决定该变量可以存储的最大和最小值。整型变量有4种基本类型,如表2-5所示。
表2-5 整型变量的基本类型
|
类 型 名 |
每个变量的一般内存空间 |
|
char |
1个字节 |
|
short int |
2个字节 |
|
Int |
4个字节 |
|
long int |
8个字节 |
char类型总是1个字节,除了char类型之外,在表2-5其他三种类型的整型变量中存储的内存量并没有标准的值。C++标准惟一要求的是该序列中的每种类型都至少要拥有与前一种类型相同的内存空间。这里显示的是在某一系统中这些类型占用的内存大小,而且是比较常见的。short int类型通常写为其缩写形式short,long int类型也通常写为其缩写形式long。这些缩写形式对应于最初的C类型名称,所以C++编译器一般都接受。初看起来,char这个名称对于整数类型来说似乎有点古怪,但如后面所述,它主要用于存储表示字符的整数代码。。
前面介绍了如何声明int类型的变量,可以以完全相同的方式声明short int和long int类型的变量。例如,用下面的语句可以定义并初始化一个short int类型的变量bean_count。
short int bean_count = 5;
也可以把这个语句改写为:
int bean_count = 5;
同样,可以用下面的语句声明一个long int类型的变量:
long int earth_diameter = 12756000L; //Diameter in meters
注意,在初始化值的后面加了一个L,表示该变量的类型是long int。如果不加上L,也不会出问题,编译器会自动把这个值从int类型转换为long int类型。但最好使初始化值的类型与变量的类型保持一致。
1. 带符号和不带符号的整数类型
short int、int和long int类型的变量可以存储正数值和负数值,所以它们是隐含的带符号的整数类型,如果要明确这一点,也可以把它们改写为signed short int、signed int和signed long int。但它们通常不使用signed关键字。
可以只使用signed关键字来表示类型,它表示signed int。但是,这并不常见,因为int只需输入三个字符。有时unsigned int会简写为unsigned,这些简写形式都来自于C。笔者个人的观点是应总是使用关键字signed或unsigned来指定底层的类型,因为这不会对其含义有影响。
不带符号的整型变量只能存储正数值,显然,这3种不带符号的类型分别是unsigned short int、unsigned int和unsigned long int。事先知道只处理正数时,就可以使用这些类型,但它们常常用于存储位模式的值,而不是数值。第3章在学习按位运算符时将详细讨论这些不带符号的类型,按位运算符用于操纵变量中的各个位。
我们需要一种区分带符号的整型字面量和不带符号的整型字面量的方式。65535可以在16位中存储为一个不带符号的值,也可以在32位中存储为一个带符号的值。不带符号的整型字面量用数字后跟字母U或u来表示,这可以应用于十进制、16进制和8进制整型字面量。如果要把一个字面量指定为unsigned long int类型,就可以使用U或u和L。

图2-3 带符号的整数和不带符号的整数
图2-3演示了16位带符号的整数和16位不带符号的整数之间的区别。在带有符号的整数中,最左边的一位表示该数值的符号。它可以是0,表示正数,也可以是1,表示负数。对于不带符号的整数,所有的位都可以看作是数据位。由于不带符号的数值总是正的,所以它没有符号位,最左边的一位是数值的一部分。
如果觉得–32768的二进制形式比较奇怪,应注意负数一般表示为2的补码形式。如附录E所述,要利用2的补码形式把正的二进制数值转换为负的二进制数值(或把负的二进制数值转换为正的二进制数值),只需反转所有的位,再加1。当然,不能把+32768表示为16位带符号的整数,因为16位带符号的整数的取值范围是–32768~ +32767。
2. 带符号和不带符号的char类型
存储为类型char的值可以是带符号的,也可以是不带符号的,这取决于编译器如何实现它。不同计算机甚至同一台计算机上的不同编译器对char类型的实现方式有所不同。如果要用一个字节存储整数值,而不是存储字符编码,就应把变量的类型显式声明为signed char或unsigned char。
注意尽管在任意给定的编译器环境下,类型char等价于signed char或unsigned char,但这3种类型实际上是不同的。当然,char、short、int、long、signed和unsigned都是关键字。
2.4.2 整数的取值范围
在C++中,内存的基本单位是字节。就C++而言,一个字节足以包含C++编译器使用的基本字符集中的任意字符,但这是没有定义的。只要一个字节可以容纳至少96个字符,就满足了C++标准。也就是说,C++标准中的一个字节至少有7位,还可以有更多位,8位系统目前比较常见。其目的是去除标准对硬件结构的依赖。如果将来生产出了16位字节的机器,C++标准将包容它,仍可以使用。不过,目前假定一个字符占用8位总是安全的。
在ANSI C++标准中,并没有规定为每种整型变量分配的内存空间。本节将讨论的内容如下:
● char类型的变量占用一个字节,可以存储基本字符集中的任一字符。
● int类型的数值所占用的字节数由被编译程序的硬件环境来定。
● 类型的signed和unsigned版本占用相同的内存空间。
● short int类型的数值至少占用与char类型相同的字节,int类型的数值至少占用与short int类型相同的字节数,long int类型的数值至少占用与int类型相同的字节数。
总之,类型char占用的字节数最少,只有一个字节,类型long占用的字节数最多。类型int介于两者之间,但占用的字节数最好符合计算机的整数运算功能。其原因是在给定的计算机上,类型int占用的字节数应使整数运算达到最高的效率。这取决于机器的结构。在大多数机器上,int类型是4个字节,但随着计算机硬件结构和性能的提高,int类型很有可能变成8个字节。
编译器给每种整数类型分配的字节数决定了该类型可以存储的值的范围。表2-6是整型变量常见的取值范围。
表2-6 整型变量的取值范围
|
类 型 |
字 节 数 |
取 值 范 围 |
|
char |
1 |
–128~127 |
|
unsigned char |
1 |
0U~255U |
|
short |
2 |
–32768~32767 |
|
unsigned short |
2 |
0U~65535U |
(续表)
|
类 型 |
字 节 数 |
取 值 范 围 |
|
int |
4 |
–2147483648~2147483647 |
|
unsigned int |
4 |
0U~4294967295U |
|
long |
8 |
–9223372036854775808L~9223372036854775807L |
|
unsigned long |
8 |
0~18446744073709551615UL |
2.4.3 整型字面量的类型
前面介绍了在整数值前面加上前缀会影响该值的基数,还提到后者U和L用于把整数表示为不带符号的类型或long类型。下面就详细讨论这些问题,理解编译器确定给定整型字面量的类型的方式。
首先,表2-7总结了整数值的前缀和后缀。
表2-7 整数值的前缀和后缀
|
后缀/前缀 |
说 明 |
|
没有前缀 |
该值是一个十进制值 |
|
前缀为0x或0X |
该值是一个十六进制值 |
|
前缀为0 |
该值是一个八进制值 |
|
后缀u或U |
该值是不带符号的类型 |
|
后缀L或l(小写字母) |
该值是long类型 |
上表中的后两项可以以任何大小写方式组合,UL、LU、uL、Lu等都是可接受的。可以使用后缀l,它是L的小写字母,但应避免使用它,因为它很容易与数字1混淆。
下面看看编译器是如何解释可以和整型字面量一起使用的前后缀的各种组合的:
● 十进制整型字面量没有前缀,如果其值位于int类型的取值范围内,它就解释为int类型。否则,就解释为long类型。
● 八进制和十六进制字面量如果没有后缀,就根据其取值范围,解释为int、unsigned int、long或unsigned long类型。
● 字面量带有后缀u或U,如果其值位于unsigned int类型的取值范围内,就解释为unsigned int类型。否则,就解释为unsigned long类型。
● 字面量带有后缀l或L,如果其值位于long类型的取值范围内,就解释为long类型,否则,就解释为unsigned long类型。
● 字面量带有U和L的大小写组合后缀,就解释为unsigned long类型。
如果字面量的值超出了可能类型的取值范围,该行为就是不确定的,编译器通常会发出一个错误消息。
注意,不能把整型字面量指定为short int或unsigned short int类型。在变量的声明中提供这些类型的初始值时,编译器会自动把字面量的值转换为需要的类型,例如:
unsigned short n = 1000;
这里根据前面的规则,字面量应解释为int类型。编译器会把这个值转换为unsigned short类型,并使用它作为变量的初始值。如果使用–1000作为初始值,就不能把它转换为unsigned short类型,因为按照定义,负数超出了该类型的取值范围。编译器肯定会发出一个错误消息。
各种整数类型的取值范围都取决于编译器。表2-6列出了常见的取值范围,但不同的编译器可能会给整数类型分配不同数量的内存空间,因此提供不同范围的值。在应用程序从一个系统移植到另一个系统上时,还需要注意类型可能的变化。
前面基本上忽略了字符字面量和char类型的变量。它们拥有一些独特的特性,所以本章将在后面介绍字符字面量和存储字符编码的变量,而先讨论整数的计算。特别是需要知道如何存储结果。





