1.5 类型、变量和算术运算
每个名字和每个表达式都有自己的类型,类型决定了名字和表达式所能执行的操作。例如,下面的声明
把inch的类型指定为int,也就是说,inch是一个整型变量。
声明(declaration)是一条语句,负责为程序引入一个新的名字,并指定该命名实体的
类型:
- 类型(type)定义了一组可能的值以及一组(对象上的)操作。
- 对象(object)是存放某类型值的内存空间。
- 值(value)是一组二进制位,具体的含义由其类型决定。
- 变量(variable)是一个命名的对象。
C++提供了若干基本类型,例如:
https://yqfile.alicdn.com/3f897c4f419a8fa5d48fb6d61957ac487712d9c4.png" >
每种基本类型都与硬件特性直接相关,其尺寸固定不变,决定了其中所能存储的值的
范围:
https://yqfile.alicdn.com/5e48b1a169b163cf2135995ed73f89168d5c10ef.png
" >
一个char变量的尺寸取决于在给定的机器上存放一个字符所需的空间(通常是一个8位的字节),其他类型的尺寸是char尺寸的整数倍。类型的实际尺寸是依赖于实现的(即在不同机器上可能不同),使用sizeof运算符可以得知该值。例如,sizeof(char)等于1,sizeof(int)通常是4。
算术运算符可用于上述这些类型的组合:
比较运算符也是如此:
除此之外,C++还提供了一些逻辑运算符:
位逻辑运算符是对它的运算对象逐位计算,所得的结果类型与运算对象的类型保持一致。一般逻辑运算符&&和||返回的是true或者false。
在赋值运算和算术运算中,C++编译器会在基本类型之间进行各种有意义的类型转换,以便它们能够自由地组合在一起,进行混合运算:
https://yqfile.alicdn.com/6b2ffaa12d0a2e208c78800985e94215eadc1936.png" >
表达式中使用的类型转换称为常用算术类型转换(usual arithmetic conversion),它的目的是确保表达式以它的运算对象中最高的精度进行求值计算。例如,一个double值和一个int值求和,执行的是双精度浮点数的加法。
注意,=是赋值运算符,而==用于相等性判断。
C++提供了好几种表示初始化的符号,比如上面用到的=。此外还有一种更加通用的形式,这种形式使用的是以花括号括起来的一个初始值列表:
https://yqfile.alicdn.com/7a622e002285c255abf87ff8020c49191415b64e.png" >
符号=是一种比较传统的形式,最早被C语言使用。但是如果拿不准的话,最好在C++中使用更通用的{}列表形式。抛开其他因素不谈,使用初始值列表的形式至少可以确保不会发生某些可能导致信息丢失的类型转换:
不幸的是,像把double转换成int或者把int转换成char这样的窄化类型转换(narrowing conversion)即使会丢失一些信息,但C++编译器不禁止这种转换,反而会隐式地自动执行。隐式窄化类型转换带来的问题是为了与C语言兼容而不得不付出的代价(见14.3节)。
常量(见1.7节)在声明时必须进行初始化,普通变量也只应在极有限的情况下不进行初始化。换句话说,在引入一个新名字时最好已经有了一个合适的值。用户定义的类型(如string、vector、Matrix、Motor_controller和Orc_warrior)可以在定义时进行隐式初始化(见4.2.1节)。
在定义一个变量时,如果变量的类型可以由初始化符号推断得到,则无需显式指定其类型:
可以使用=初始化形式与auto配合,因为在此过程中不存在可能引发错误的类型转换。
当我们没有明显的理由需要显式指定数据类型时,一般使用auto。在这里,“明显的理由”包括:
- 该定义位于一个较大的作用域中,我们希望代码的读者清楚地知道其类型;
- 我们希望明确规定某个变量的范围和精度(比如希望使用double而非float)。
使用auto可以帮助我们避免冗余,并且无需再书写长类型名。这一点在泛型编程中尤其重要,因为在泛型编程中程序员很难知道对象的确切类型,类型的名字也可能相当长
(见10.2节)。
除了传统的算术运算符和逻辑运算符之外,C++还提供了其他一些可用于改变变量值的运算符:
这些运算符简洁明了,使用广泛。