本章将介绍C++的基础数据类型,主要涉及下列方面:
- 变量的声明、初始化、赋值
- 整数字面量
- 浮点数如何计算
- 变量类型转换
- 字符相关
- auto关键字
Variables, Data, and Data Types
这里先给出变量的定义:有名字的一块内存,这个变量的类型决定了程序如何解释这块内存,如何使用这块内存。并且类型由一系列关键字组成
基础变量分整型和浮点型两大类
Defining Integer Variables
要说明几点:
- 第九行只是声明,初始化由编译期完成,此处进行默认初始化(声明 ≠ 初始化)
- 初始化有三种形式,{},(),=,大多数情况下都没有区别
- {}会检查数据存不存在精度丢失的情况,如果存在narrowing conversion则报错
Signed Integer Types 有符号整型
这是书上给出的经典值(常见值),而标准只要求了最小大小(表里包含无符号整型):
表里每一小栏靠前的名字是更常用的,即short比short int常见
我们可以使用sizeof关键字查看类型所占字节数
Unsigned Integer Types 无符号整型
和对应的有符号类型占用相同的内存
书上提到char应当用来存储字符类型,用signed/unsigned char来存储数字,std::byte来存储二进制数据
char和signed char不是一种类型!这是整型里的例外!它的有符号性依赖于平台实现!
Fundamental types - cppreference.com
我们可以用limits里面的一个模板类来查看自己机器的char是不是有符号类型(取决于机器对数字的编码)
Zero Initialization
{}和{0}的初始化效果一样,都是使数值类型初始化为0,{}对所有基本数值类型都适用
Defining Variables with Fixed Values 定义常量
加个const即可
Integer Literals 整数字面量
Integer literal - cppreference.com
书上整理的没有cppref系统,这个不常见,用的时候查就可以了
需要知道常见的:u代表无符号,l/L代表long
'作为分割符可以增强数字可读性:
上面介绍了十进制的后缀,下面看看二进制、八进制和十六进制的写法
Calculations with Integers 整数运算
加减乘除加上取余:+-*/%
注意整数除法结果永远是整数,上图是除法和取余的对比
Assignment Operations 赋值操作
简单的等号不讲了,可以看看上面的复合赋值操作
Incrementing and Decrementing Integers 整数自增自减
前置后置的区别不再赘述,谨记一条,不要在一条语句中多次自增自减,宁愿写几行也不要带来歧义
Defining Floating-Point Variables 定义浮点变量
浮点数存储大多遵循IEEE754标准,但大多数情况下我们只用double,够大也够快
浮点数默认字面量也是double类型
Floating-Point Calculations 浮点数计算
Mathematical Constants 数学常数
用到再查吧
尝试输出了一下发现都是6位有效数字
Mathematical Functions 数学函数
用到再查,记得定义在<cmath>里
Invalid Floating-Point Results
主要是由于除数为0造成的无意义数
std::isinf() and std::isnan()可以帮助我们排错
Mixed Expressions and Type Conversion 混合表达式和类型转换
我们不能预知所有的数据类型,总会有不同类型的数值在同一个表达式中出现的时候,此时C++应用一套隐式的类型转换系统(整型提升Integral promotion)
Implicit conversions - cppreference.com
常见的类型排序一下如上表所示,当不同类型混合运算时,总会把等级低的类型转换成高等级类型后再进行运算,表中序号越小代表等级越高
经典的例子就是有无符号整数相加减:
The phenomenon where the result of a subtraction of unsigned integers wraps around to very large positive numbers is sometimes called underflow.
这里引入了下溢的概念,指的是无符号数的减法结果变成了非常大的正数,本质是无符号数无法存储负数,只能取余后循环到一个大正数(可以了解一下计组中数字的表示)
另外一个问题就是隐式类型转换,我们将double类型的z赋值给y后,y存储的值将是小数部分去掉后的z
我们知道有这么个机制即可,写代码时不应该依靠这种隐式转换,经常导致精度损失
不想出现这种情况的话,我们应该使用下面介绍的显式类型转换
Explicit Type Conversion 显式类型转换
static_cast<type_to_convert_to>(expression)
上面是cast的语法,又叫conversion,static_cast表明这种类型转换是在编译期处理的,所以我们可以预测后面还会介绍运行期的动态cast
上面是两处具体应用,在{}初始化里使用显式cast也不会报错了
std::round(), lround(), and llround() functions from <cmath>
这几个四舍五入的函数返回的结果都是整数
std::round, std::roundf, std::roundl, std::lround, std::lroundf, std::lroundl, std::llround, std::llroundf - cppreference.com
总之显式类型转换不应该出现太多,否则可能是选用的类型不对需要重新设计
Old-Style Casts 旧风格cast
(type_to_convert_to)expression
实际上还是很多见,语句比较短的时候还是可以用的
但是现代C++不推荐这种写法,语义不明确
Formatting Strings 字符串格式化
Formatting Stream Output 流输出格式化
这种方式是用来控制流的,即ostream,通过改变对象的状态来控制输出格式
<ios> and <iomanip>头文件里有控制符的一些函数
- std::hex (produces hexadecimal numbers)
- std::scientific (enables exponent notation for floatingpoint numbers)
- std::setw() (used to format tabular data)
在之前这个示例中,可以看到以下输出
但有时候我们不想要那么高的精度,我们尝试保留两位有效数字significant digits
但是C++20有更为轻量强大的方式std::format()来进行字符串的格式化
String Formatting with std::format()
但是精度好像更高了
这是因为format保证double最高有16位有效数字,当然我们也可以在大括号里对数据指定格式
Format Specifiers
格式控制说明符是参考python中的,Standard format specification - cppreference.com
具体用法可以参考:std::format() - 知乎
书里提到的简化说明:
如果写了不合法的格式控制,format将会抛出异常
我愿称之为C和Python杂交版本的printf,格式控制符是一个常用才能记住的东西,下面举一个例子看一下各种控制符的用法与效果
圈起来{:.2f}的原因是之前经常遇到小数点后保留几位数字的要求,这个和precision还不是一个概念,f代表fixed是固定小数点的意思
Formatting Tabular Data
深色字段表示用于表格化数据的输出控制:
width表示输出最小宽度,如果不够这个宽度将使用0或者字符进行补全(具体看数据类型);
align表示补全字符的插入位置是左右还是中间
对string, character, and Boolean默认对齐是靠左对齐
Formatting Numbers
我们可以指定符号、精度、输出进制等格式,还是来看一个例子:
Argument Indexes
冒号左边也有用,作为参数索引,指定参数输出的顺序
这个参数还可以用于一个数输出多次
Finding the Limits
直觉上我们用numeric_limits<type>::min()和numeric_limits<type>::max()来获取某种算术类型的最大最小值
但是浮点数中的min保存的是最小正数(因为IEEE754保存的精度有限),这里的最小指的是精度最大能够区分的最小,lowest()保存的才是最大的负数
这种情况在整数不存在
Finding Other Properties of Fundamental Types
这里是输出了一些无穷值和NAN
其他的可以去reference查
Working with Character Variables
The auto Keyword
值得注意的是不同版本的C++对于auto初始化有不同的解释,C++17之后只要不是初始化列表类型,都可以用大括号初始化,initializer_list需要用=初始化;之前的版本就不要用大括号初始化auto变量
EXERCISES
beginning-cpp20/Exercises/Modules/Chapter 02 at main · Apress/beginning-cpp20 · GitHub
官方答案可以看这里
2-1
对一些美国常用单位进行转换的小程序,1英尺=12英寸,要求输入英寸转换成英尺和英寸
2-2
2-4
计算BMI的小程序,只不过输入要求是英镑和英寸
2-5
意思是小数点后两位及以上的BMI是没有意义的精度,保留一位即可,想到之前的format里有f控制符
2-6
复现表格2-6
2-7
在上一题的表格中加一行sin(pi/4),要求使用科学记数法表示,且小数点后保留五位,指数的e还要大写
2-8
比较两个正数的大小但是不用第五章的条件语句,仅使用本章的运算符即可实现
这我也是看答案的,当x>y时y/x为0而x/y不为0,这是核心思路