2. 变量和基本类型
数据类型是程序的基础,决定了能够在数据上执行的操作。C++定义了集中基本的内置类型(字符、整型、浮点数...),也提供了自定义数据类型的机制。
2.1 基本内置类型
2.1.1 算术类型 整型(布尔型和字符型)以及浮点型
2.1.2 类型转换
2.1.3 字面值常量 整型和浮点型字面值、字符和字符串字面值、转义序列
2.2 变量
2.2.1 变量定义 类型说明符+变量名、列表初始化、默认初始化
2.2.2 变量声明和定义的关系 extern仅声明、变量能且只能被定义一次但可多次声明
2.2.3 标识符
2.2.4 名字的作用域
2.3 复合类型
2.3.1 引用 &d
- 只能绑定在对象上,不能与字面值或表达式的计算结果绑定在一起
- 引用的类型必须和引用的对象的类型一致(两个例外:1.const的引用可以绑定任意表达式,只要表达式的结果能够转换成引用的类型)
2.3.2 指针 *d
2.3.3 理解复合类型的声明 指向指针的指针、指向指针的引用
2.4 const限定符:在const变量的声明和定义中添加extern关键字实现多个文件共享const对象
2.4.1 const的引用:const的引用绑定的对象不一定为const对象
2.4.2 指针和const
- const的指针指向的对象不一定为const对象
- const对象的地址只能由const指针存放
2.4.3 顶层const
顶层const(指针为const指针)底层const(指针指向的对象为const对象)
2.4.4 constexpr和常量表达式
2.5 处理类型
2.5.1 类型别名:typedef、using
2.5.2 auto类型说明符:自动推断变量类型
2.5.3 decltype类型提示符 :选择并返回操作数的数据类型
2.6 自定义数据结构
2.6.1 定义Sales_data类型:struct
2.6.2 使用Sales_data类
2.6.3 编写自己的头文件:.h文件
2.1 基本内置类型
包括算术类型和空类型,算术类型包含了字符、整型数、布尔值和浮点数;空类型不对应具体的值,在特殊场合(比如函数不返回任何值时返回空类型)
2.1.1 算术类型
包括整型(字符和布尔类型)和浮点型。
上表列出了C++标准规定的尺寸的最小值 ,某一类型所占的比特数不同,能够表示的数据范围也不一样。其中基本的字符类型是char,一个char的大小和一个机器字节一样(1字节=8比特),其他字符类型用于扩展字符集。
整型可以分为带符号的 signed 和无符号的 unsigned 两种。其中int,short,long和long long都是带符号的,通过在前面加上 unsigned 得到无符号类型。无符号类型所有比特都用来存储值。
选择类型的注意事项:
- 当数值不可能为负时,选择无符号类型;
- 使用int执行整数运算,如果数值超过int的表示范围选择long long;
- 算术表达式中不要使用char或bool;
- 使用double执行浮点数运算,float通常精度不够。
2.1.2 类型转换
- 非布尔类型值赋给布尔类型时,初始值为0则结果为false,否则结果为true;
- 布尔值赋给非布尔类型时,初始值为false则结果为0,否则结果为1;
- 浮点数赋给整数类型,结果保留浮点数中小数点之前的部分;
- 整数值赋给浮点类型,小数部分记为0(若整数所占空间超过浮点类型的容量,精度可能有损失);
- 赋给无符号类型一个超出表示范围的值,结果时数值取模后的余数;
- 赋给带符号类型一个超出表示范围得知,结果时未定义的(程序可能继续工作也可能崩溃)。
2.1.3 字面值常量
每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型。
整型和浮点型字面值:
字符和字符串字面值:
字符串字面值的类型实际上是由常量字符构成的数组,编译器在每个字符串的结尾处添加一个空字符(‘ ’),因此字符串字面值的实际长度比它的内容多1。
转义序列:
指定字面值的类型:
布尔字面值和指针字面值:
布尔字面值:true、false
指针字面值:nullptr
2.2 变量
提供一个具名的、可供程序操作的存储空间,变量 variable 和对象 object 一般可以互换使用。
对象是指一块能存储数据并具有某种类型的内存空间。
2.2.1 变量定义
类型说明符+变量名,变量名之间以逗号分隔,最后以分号结束。
列表初始化:(用花括号初始化变量)
当使用列表初始化且初始值存在丢失信息的风险时,翻译器将报错。
默认初始化:
- 内置类型的变量没有被显式初始化时,定义于任何函数体之外的变量被初始化为0,定义在函数体内部的变量不被初始化;
- 每个类各自决定其初始化对象的方式,绝大多数类无需显式初始化而定义对象。
2.2.2 变量声明和定义的关系
C++支持分离式编译机制,允许把程序拆分成多个逻辑部分来写,为了支持分离式编译,C++将声明 declaration(使名字为程序所知,如果文件想使用别处定义的名字必须包含对那个名字的声明)和定义 definition(创建与名字关联的实体)分开。
仅声明一个变量时需要在变量名前添加关键字extern,而且不要显式地初始化变量(包含显式初始化地声明即成为定义),这样会抵消extern的作用。
变量能且只能被定义一次,但可以被多次声明。
定义:多个文件中使用同一个变量时,变量的定义必须出现在而且只能出现在一个文件中。
声明:多个文件中使用同一个变量时,其它用到该变量的文件必须进行声明,而且不能重复定义。
C++是一种静态类型statically typed语言,在编译阶段检查类型,检查类型的过程称为类型检查。由于对象的类型决定了对象所能参与的运算,如果试图执行类型不支持的运算编译器将报错,但需要编译器知道每一个实体对象的类型,因此在使用某个变量之前必须声明类型。
2.2.3 标识符
- 字母、数字、下划线组成且只能以字母或下划线开头,大小写敏感;
- 变量名使用小写字母;
- 用户自定义的类名以大写字母开头;
- 标识符由多个单词组成时使用下划线区分。
2.2.4 名字的作用域
同一个名字在不同的作用域中可能指向不同的实体(变量、函数、类型等),大多数作用域以花括号分隔(for循环内定义的变量只能在for循环内使用)。名字的有效区域始于名字的声明语句,终于声明语句所在的作用域末端。
2.3 复合类型
基于其他类型定义的类型 。
声明语句:基本数据类型+声明符(变量名)
2.3.1 引用
引用为对象另外起了一个名字,引用类型 引用 另外一种类型,通过将声明符写成&d形式定义引用类型,其中d是声明的变量名。(引用必须初始化)
- 引用并不是对象,而是为一个已经存在的对象所起的另一个名字。
- 引用只能绑定在对象上,而不能与字面值或者某个表达式的计算结果绑定在一起。
引用的类型必须与其所引用的对象的类型一致,除了以下两种情况:
- 初始化常量引用时,允许用任意表达式作为初始值,只要表达式的结果能转换成引用的类型即可(允许为一个常量引用绑定非常量的对象、字面值、表达式);
2.3.2 指针
通过将声明符写成 *d 形式定义指针类型,其中d是变量名。
指针和引用的不同点:
- 指针本身就是一个对象,允许对指针赋值和拷贝,引用不是一个对象;
- 在指针的生命周期内,它可以先后指向几个不同的对象,而一旦定义了引用就无法令其再绑定到另外的对象;
- 指针无须在定义时赋初值,在块作用域内定义的指针如果没有被初始化将拥有一个不确定的值。
获取对象的地址:
指针存放某个对象的地址,用取地址符(&)获取地址。
指针值:
- 指向一个对象;
- 指向紧邻对象所占空间的下一个位置;
- 空指针(指针没有指向任何对象);
- 无效指针
第2,3种没有指向具体对象,不能访问此类指针对象,也不能对其解引用。
利用指针访问对象:
使用解引用符(*)访问指针指向的对象,也可以通过解引用指针对指针指的对象赋值。
空指针:
生成空指针的方法:
最好使用nullptr避免使用NULL
赋值和指针:
给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。
赋值改变等号左侧的对象。
void* 指针:
可以存放任意对象的地址,但不能直接操作void*指针指向的对象,因为不确定对象的类型。
2.3.3 理解复合类型的声明
类型修饰符(&*)是声明符的一部分。
指向指针的指针:
指向指针的引用:
2.4 const限定符
const对象一旦创建后值就不能再改变,const对象必须初始化。
默认状态下,const对象仅在文件内有效。为了在多个文件中共享,可以在const变量的声明和定义中添加extern关键字,这样只需定义一次。
这里extern的作用是知名bufSize不是本文件所独有,它的定义将在别处出现(在之前的章节中使用extern实现只声明不初始化变量)
2.4.1 const的引用常量引用即为对const的引用
1. 初始化常量引用时,允许用任意表达式作为初始值,只要表达式的结果能转换成引用的类型即可(允许为一个常量引用绑定非常量的对象、字面值、表达式)(const int的引用可以绑定int类型的变量):
r1是const int类型,i是int类型,类型不同也可以引用;
r2绑定一个字面值,r3绑定一个表达式,都是合法的。
2. const的引用可能引用一个并非const的对象
这里r2为const的引用,但是i并不是const的对象。
2.4.2 指针和const
存放常量对象的地址,只能使用指向常量的指针(const int类型的变量地址只能由const int类型的指针存放,但是const int类型的指针可以存放int类型的变量地址)。
const指针:
可以把指针本身定为常量,常量指针必须初始化,初始化完成后它的值就不能再改变了,把*放在const之前说明指针是一个常量,即不变的是指针本身的值而不是指向的那个值。
【从右向左阅读有利于弄清声明的含义】:离curErr最近的是const,即curErr本身是一个常量对象,接下来是*,说明curErr是一个常量指针。同理可得pip是一个常量指针,它指向的对象是一个双精度浮点型常量。
2.4.3 顶层const
顶层const:指针本身是个常量指针(比如p1本身是个常量指针)
底层const:指针所指的对象是一个常量(比如p2是个普通指针,但是指向的对象是常量)
2.4.4 constexpr和常量表达式
常量表达式const expression是指值不会改变并且在编译过程就能得到计算结果的表达式:
constexpr变量:
constexpr变量:声明为constexpr的变量类型一定是一个常量,可以让编译器验证变量的值为一个常量表达式,必须用常量表达式初始化。
字面值类型:
- 算术类型、引用和指针都属于字面值类型,自定义类、IO库等则不属于字面值类型
- 只有字面值类型能被定义成constexpr
- constexpr指针的初始值必须是nullptr或者0,或者存储于某个固定地址中的对象(函数体内的变量不是存放在固定地址中,不能使用constexpr指针)
- 如果constexpr声明中定义了一个指针,constexpr只修饰指针而不修饰指针指向的对象
2.5 处理类型
2.5.1 类型别名
使复杂的类型名字简单明了,易于使用。
1. 使用typedef定义类型别名:
2. 使用别名声明定义类型的别名:
类型别名就是类型的名字
pstring是char *的类型别名意思是指向char的指针,因此cstr是指向char的常量指针,ps是一个指向指针的指针,ps指向的指针是指向char的常量指针。不可以把类型别名替换进去理解含义,pstring的基本类型是指针,替换后会发生变化。
2.5.2 auto类型说明符
auto:自动推断变量类型,auto定义的变量必须有初始值。
2.5.3 decltype类型提示符
decltype:选择并返回操作数的数据类型,分析表达式得到它的类型但不实际计算表达式的值。
假如f()的返回值为int,sum的类型则为int,在这个过程中没有实际调用f()
2.6 自定义数据结构
2.6.1 定义Sales_data类型
Sales_data为类名,花括号中的内容为类体。在类体后面可以紧跟变量名,定义该类的对象(最好不要)。
类数据成员data member:Sales_data中的数据成员包括bookNo,units_sold,revenue。
另外也可以通过class定义数据结构。
2.6.2 使用Sales_data类
为了实现两次交易相加结果的功能,需要使用Sales_data类。程序大体结构如下:
添加Sales_data对象:Sales_data data1, data2;
Sales_data对象读入数据:
判断ISBN并计算交易相加结果:
2.6.3 编写自己的头文件
- 头文件通常包含只能被定义一次的实体,如类、const、constexpr变量
- 类所在头文件的名字应该与类的名字一样
- 头文件也可能需要用到其它头文件的功能,比如Sales_data.h必须包含string.h头文件,并且使用Sales_data类的程序也要包含string.h头文件
预处理器:
在编译之前执行的一段程序(比如#include),预处理变量有两种状态:已定义和未定义,#define指令把一个名字设定为预处理变量,#ifdef和#ifndef检查某个指定的预处理变量是否定义(#ifdef在变量已定义时为真,#ifndef在变量未定义时为真),当检查结果为真时执行后续操作直到遇到#endif指令。以下是防止重复包含的代码:



