《C++ Primer 中文版 第 5 版》读书笔记
《C++ Primer 中文版 第 5 版》
第Ⅰ部分 C++基础 1
- C++是一种静态数据类型语言, 它的类型检查发生在编译时. 因此, 编译器必须知道程序中每一个变量对应的数据类型
第 1 章 开始
1.1 编写一个简单的C++程序
- 操作系统通过调用 main 来运行 C++ 程序
- main 函数的返回值必须为 int
- return 用来结束函数的执行
- 集成开发环境(Integrated Development Environment, IDE) :将编译器与其他程序创建和分析工具包装在一起
- 源文件(source file):存储程序源码的文件
1.2 初识输入输出
-
iostream 库包含两个基础类型 istream 和 ostream,分别表示输入流和输出流
-
一个流就是一个字符序列,是从IO设备读出或写入IO设备的
-
标准输入输出对象
- cin:istream类型的对象,称为标准输入
- cout:ostream类型的对象,称为标准输出
- cerr:ostream类型的对象,用来输出警告和错误信息,称为标准错误
- clog:ostream类型的对象,用来输出程序运行时的一般信息
-
一般将一个程序的所有 #include 指令都放在源文件的开始位置
-
输出运算符(<<)
-
接受两个运算对象,左侧的运算对象必须是一个ostream对象,右侧的运算符对象是要打印的值
-
此运算符将给定的值写到给定的ostream对象中
-
此运算符返回其左侧的运算对象 (即ostream对象),因此第一个运算符的结果成为了第二个运算符的左侧运算对象
cout << "Enter two numbers: " << endl; //等价于下面的表达式 (cout << "Enter two numbers: ") << endl;
-
-
输入运算符(>>)
- 接受一个istream对象作为其左侧运算对象, 接受一个对象作为其右侧运算对象
- 从给定的istream对象读入数据, 并存入给定的对象中
- 与输出运算符类似, 输入运算符返回其左侧运算对象作为其计算结果
-
标准库定义的所有名字都在命名空间(namespace) std 中, 命名空间可以帮助我们避免不经意的名字定义冲突
-
读取数量不定的输入数据
#include <iostream> using namespace std; int main() { //如果不确定用户输入数据的个数,这时可以使用while循环来不断地读取数据直至没有新的数据为止 //计算用户输入数字的和 int sum = 0, value = 0; //当使用istream对象作为条件时,其效果是检测流的状态 //如果流是有效的,即流未遇到错误,则检测成功 //当遇到文件结束符(Ctrl+z, 然后输入Enter --- Windows),或者无效输入时(其他类型),istream对象的状态会变为无效 while (cin >> value) { sum += value; } cout << "sum = " << sum << endl; system("pause"); return 0; }
1.5 类简介
- 头文件通常根据其中定义的类的名字来命名, 通常使用 .h 作为头文件的后缀
- 包含来自标准库的头文件时, 应该用 <> 包围头文件名. 对于不属于标准库的头文件, 则用 " " 包围
术语表
- 参数(实参, argument): 向函数传递的值
- 赋值(assignment): 抹去一个对象当前的值, 用一个新值取代
- 程序块(block): 零条或多条语句的序列, 用花括号{}包围
- 缓冲区(buffer): 一个存储区域, 用于保存数据. IO设施通常将输入 (或输出) 数据保存在一个缓冲区内, 读写缓冲区的动作与程序中的动作是无关的
- 编辑->编译->调试(edit->compile-debug): 使程序能正确执行的开发过程
- 文件结束符(end-of-file, eof): 系统特定的标识, 指出文件中无更多数据了
- iostream头文件: 提供面向流的输入输出的标准库类型
- 未初始化的变量(uninitialized variable): 未赋予初值的变量. 类类型的变量如果未指定初值, 则按类定义指定的方式进行初始化. 定义在函数内部的内置类型变量默认是不初始化的, 除非有显式的初始化语句. 试图使用一个未初始化的变量的值是错误的. 未初始化变量是bug的常见成因.
第 2 章 变量和基本类型
2.1 基本内置类型
-
算数类型: 算数类型分为两类: 整型(integral type, 包括字符和布尔类型在内) 和 浮点型
-
内置类型的机器实现
-
计算机以比特(bit) 序列存储数据, 每个比特非0即1
-
大多数计算机以2的整数次幂个比特作为块来处理内存, 可寻址的最小内存块称为字节(byte), 存储的基本单元称为字(word)
-
大多数机器的字节由8比特构成, 字则由4或8字节构成, 也就是32或64比特
-
大多数计算机将内存中的每个字节与一个数字(被称为"地址", address)关联起来, 如下图所示(字节为8比特, 字为32比特的机器), 每个字节拥有一个地址
- 为了赋予内存中某个地址明确的含义, 必须首先知道存储在该地址的数据的类型, 类型决定了数据所占的比特数以及该如何解释这些比特的内容
-
-
带符号类型和无符号类型: 除去布尔型和扩展的字符型之外, 其他整型可以划分为带符号(signed)的和无符号(unsigned)两种. 带符号类型可以表示正数, 负数和0, 无符号类型仅能表示大于等于0的值
-
类型unsigned int可以缩写为unsigned
-
执行浮点数运算选用double, 这是因为float通常精度不够, 而且双精度浮点数和单精度浮点数的计算代价相差无几. 事实上, 对于某些机器来说, 双精度运算甚至比单精度还快
-
类型转换
- 当我们赋给无符号类型一个超出它表示范围的值时, 结果是初始值对无符号类型表示数值总数取模后的余数
- 当我们赋给带符号类型一个超出它表示范围的值时, 结果是未定义(undefined)的, 此时, 程序可能继续工作, 可能崩溃, 也可能生成垃圾数据
- 当一个算数表达式中既有无符号又有int值时, 那个int值就会转换成无符号数
- 把负数转换成无符号数类似于给无符号数赋一个负值, 结果等于这个负数加上无符号数的模
- 切勿混用带符号类型和无符号类型
-
字面值常量
-
一个形如42的值被称为字面值常量(literal), 这样的值一望而知. 每个字面值常量都对应一种数据类型, 字面值常量的形式和值决定了它的数据类型
-
以0开头的整数代表八进制数, 以0x或0X开头的代表十六进制数
-
字符串字面值的类型实际上是由常量字符构成的数组(array)
-
编译器在每个字符串的结尾处添加一个空字符('\0'), 因此, 字符串字面值的实际长度要比它的内容多1
-
当书写的字符串字面值比较长, 写在一行里不太合适时, 就可以采用分开书写的方式:
cout << "a really, really long string literal" "that spans two lines" << endl;
-
C++语言规定的转义序列包括:
-
2.2 变量
- 变量提供一个具体的, 可供程序操作的存储空间
- C++中的每个变量都有其数据类型, 数据类型决定着变量所占内存空间的大小和布局方式, 该空间能存储的值的范围, 以及该变量能参与的运算
- 初始值
- 当对象在创建时获得了一个特定的值, 我们说这个对象被初始化(initialized)了
- 在C++语言中, 初始化和赋值是两个完全不同的操作. 初始化的含义是创建变量时赋予其一个初始值, 而赋值的含义是将对象当前的值擦除, 并以新值来代替
- 默认初始化
- 如果定义变量时没有指定初值, 则变量被默认初始化(default initialized), 此时变量被赋予了默认值. 默认值到底是什么由变量类型决定, 同时定义变量的位置也会对此有影响
- 如果是内置类型的变量未被显式初始化, 它的值由定义的位置决定. 定义于任何函数体之外的变量被初始化为0
- 一个未被初始化的内置类型变量的值是未定义的, 如果试图拷贝或以其他形式访问此类值将引发错误
- 未初始化的变量含有一个不确定的值, 使用未初始化变量的值是一种错误的编程行为并且很难调试
- 建议初始化每一个内置类型的变量. 虽然并非必须这么做, 但如果我们不能确保初始化后程序安全, 那么这么做不失为一种简单可靠的方法