第2章 变量和基本类型
2.1基本内置类型
基本内置类型包括,算术类型(arithmetic type)和空类型(void),当函数不返回任何值时,使用空类型作为返回类型。
2.11算术类型
算术类型分为两类:整形(包括布尔型)和浮点型
不同算术类型所占的内存空间不尽相同,在不同机器上也有一定差别,C++标准只规定了尺寸的最小值。
- bool 布尔类型 最小尺寸未定义(常见为16位)
- char 字符 最小尺寸8位
- wchar_t 宽字符 最小尺寸16位
- short 短整型 最小尺寸16位
- int 整型 最小尺寸16位(但常见的为32位)(整数默认类型)
- long 长整型 最小尺寸32位
- long long 长整型 最小尺寸64位
- float 单精度浮点数 6位有效数字
- double 双精度浮点数 10位有效数字(浮点数默认类型)
- long double 扩展精度浮点数 10位数字
以下是常见的算术类型字节数。
布尔类型只有两个取值,true(非0)和false(0),大多占一个字节
char16_t 在字符前声明u
char32_t 在字符前声明U 二者均为无符号类型,Unicode字符。
选择类型的经验准则:
- 明知数值不可能为负时,选用无符号类型
- 一般使用int执行整数运算,short经常范围不足,int范围不足时改用long 或者long long
- 浮点数运算首选double,float常常会精度不足
2.1.2类型转换
将对象从一种给定的类型转换(convert)为另一种相关的类型。
类型所能表示的值的范围决定了转换的过程:
- 当我们把一个非布尔类型的算数值赋给布尔类型时,初始值为0则结果为false,初始值非0则结果为true
- 当我们把一个布尔值赋给非布尔类型时,初始值为false则结果为0,初始值为true则结果为1
- 当我们把一个浮点数赋给整数时,将会只保留整数部分,截断小数部分
- 当我们把一个整数值赋给浮点数时,继承整数部分,小数部分记为0,如果整数所占的空间超过了浮点类型的精度范围,可能会产生精度损失。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示总数(最大值加一)取模后的余数。
- 当我们赋给有符号类型一个超出它表示范围的值时,结果是未定义的。
1、非赋值运算的自动类型转换
转换核心原则:保证运算的精度不降低
- 水平方向的转换:char、short转为int,unsigned short 转为unsigned,long转为unsigned long,float转为double,这样都不会损失精度
- 竖直方向的转换:int转为unsigned,unsigned 转为 unsigned long,unsigned long转为double 竖直方向的转换包括有符号转为无符号,整型转为浮点型,可能存在精度丢失等问题,慎重进行。
2、赋值运算的类型转换
进行赋值运算时,将赋值号 右侧表达式的类型 自动转换成 左侧变量 的类型,右类型转左类型。
3、强制类型转换
建议:不要进行隐式的数据转换,可能产生不可知的问题。避免无法预知和依赖于实现环境的行为,写出可移指的程序。
2.1.3 字面值常量
整型和浮点型字面值
整型字面值可以写成十进制、八进制、十六进制。0开头的表示八进制数,0x或0X(分别对应a~f大小写)开头的表示十六进制数。整型字面值一般默认为int
浮点型字面值表现为一个小数或以科学计数法表示的指数,指数部分用e或E表示。浮点型字面值一般默认为double,需要为float时,在后面加上f
字符和字符串字面值
单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符称为字符串型字面值。字符串字面值实际上是由常量字符构成的数组,每个字符串的末尾会添加一个空字符('\0'),所以字符串字面值的实际长度要比其内容多1.
当两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体。
转义序列
有两类字符无法直接使用,一类是不可打印字符,如退格或其他控制字符,因为它们没有可视的图符;另一类是在C++中有特殊含义的字符(单引号、双引号、问号、反斜杠)。
要表示这两类字符,都要用到转义序列(escape sequence),转义序列均以反斜线(\)开始。C++中的转义序列包括:
换行符 \n (new line) 横向制表符 \t (transverse) 纵向制表符\v(vertical) 退格符 \b(back) 报警(蜂鸣)符 \a 双引号\'' 单引号\' 反斜线\\ 问号\? 回车符\r 进纸符\f
泛化的转义序列可以用\后跟八进制数字 或者 \x后跟十六进制数字表示,一般默认数字对应的是ASCII字符。\后面最多只能跟3个八进制数字,\x后没有这个限制。通常与指定字面值类型结合使用。
指定字面值的类型
前缀用于修饰字符字面值
- u 表示Unicode 16字符 类型为char16_t
- U 表示Unicode 32字符 类型为char32_t
- L 表示宽字符 类型为wchar_t
- u8 表示UTF-8(仅用于字符串字面常量) 类型为char
后缀用于修饰数字字面值
整型字面值后缀:
- u or U 表示unsigned
- l or L 表示long
- ll or LL 表示long long
- u 和 l 、ll之间可以组合为ul、ull分别表示unsigned long 和 unsigned long long
浮点型字面值后缀:
- f or F 表示float
- l or L 表示long double
布尔字面值为true 和 false ,指针字面值为nullptr
2.2变量
变量是一个具名的、可供程序操作的存储空间。
2.2.1 变量定义
变量定义的基本形式是:类型说明符(type specifier),随后紧跟一个或多个变量名,变量名之间以逗号分隔,最后以分号结束。每个变量名的类型都由类型说明符指定,定义时还可以给一个或多个变量赋值。
初始化(initialized)
C++中的初始化和赋值是两个完全不同的操作,初始化是创建一个内存空间,该内存空间与某个变量绑定,然后往该内存空间写入初始值。赋值是把对象的当前值擦除,写入一个新的值。
列表初始化
定义变量a等于0 可以用以下四种方法
- int a = 0;
- int a = {0};
- int a {0};
- int a (0);
默认初始化
定义变量时如果没有指定初始值,则变量被默认初始化。此时变量被赋予了默认值,默认值由变量类型和定义变量的位置决定。
如果内置类型(算术类型)的变量未被初始化,变量定义在任何函数体之外时默认为0;定义在函数体内部时,变量将不被初始化,此时变量的值未被定义。对于string类型的变量,因为string类型本身接受无参数的初始化方式,所以不论定义在函数内还是函数外都被默认初始化为空串。
自定义类的变量的初始值由类自己决定,由类提供默认值或者显式初始化。
建议定义时就初始化每一个内置类型的变量。
2.2.2 变量声明和定义的关系
C++支持分离式编译,允许将程序分割为若干个文件,每个文件可被独立编译。例如std::cout和std::cin定义于标准库文件,我们却可以使用。
为了支持分离式编译,C++将声明(declaration)和定义(definition)区分开来。
- 声明使得变量名为程序所知,规定了变量的类型和名字。一个文件如果想使用别处定义的名字则必须包含对那个名字的声明,例如声明#include <iostream>
- 定义负责创建与名字关联的实体,也规定了变量的类型和名字,还申请了存储空间,也许会为变量赋初始值。
区分声明与定义:
- 先看有没有显示初始化,有则是定义;
- 再看有没有extern ,没有初始化且有extern 则为声明,否则为定义。
- 例如extern int i; 为声明 int j; 为定义 extern int i = 0; 为定义(包含了初始化的声明算作定义,也叫声明并定义)
变量只能定义一次,不能重复定义,但可以被多次声明。所以变量的定义只能出现在一个文件中,多个文件均可以声明然后调用。
C++是一种静态类型语言(statically typed),在编译阶段检查类型是否合法(type checking)
2.2.3 标识符
C++标识符(identifier)