Part A. 学习笔记
本章内容:
- 使用变量的方法
- 在C++中进行算术运算
- C++中的数据类型转换
3.0 C++内置类型内容大纲
3.1 简单变量
3.1.1 变量名
程序必须记录:数据储存在哪、数据的值、该数据是什么类型
一些命名规则:用两个下划线(即__a)打头、一个下划线和字母打头(即_A)的名称被保留给实现;用一个下划线(即_a)打头的名称是全局标识符,也会被保留给实现
变量的常用命名方案:例如要把一个变量命名为my value,则可以使用 myValue 或者 my_value
3.1.2 整型
位与字节
1. 什么是位(bit)
- 可以简单理解为计算机存储数据的最小单位,一个bit仅能存储一个数字
- 计算机系统默认使用二进制来保存所声明的变量,在二进制的情况下,这个数字是0或1
2. 什么是字节(byte)
1个字节含有8个bit,当使用二进制时,示意图如下:
0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
这个字节储存的数字为15,原因如下图所示:
二进制 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
十进制 | 0*2^8 | 0*2^6 | 0*2^5 | 0*2^4 | 1*2^3 | 1*2^2 | 1*2^1 | 1*2^0 |
将十进制读取后的数字相加,即可得到数字15
3. 整型溢出原理
1)有符号整数的溢出
补码原理:符号整数表示负数的方式
假设一个数字为3,它在一个字节中的的储存形式为:
0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
将它的0全部变成1,1全部变成0,再加上1,则有:
1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
直接对以上两者做二进制加法,我们得到:
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
本来应该为1后边跟9个0,但是无法储存这个数字,因为一个字节只有8个位(这个就是内存溢出),故只能得到8个0,此时,这个字节里面储存的数为0
我们知道,3+(-3)=0,故将一个二进制储存的数的0全部变成1,1全部变成0,再加上1这样表示负数的方式,被称为补码
这样带来的副作用是,一个字节的内存,即8bit的内存只能存储6bit的有效位数,此时能存储的最大、最小值为 ± (2^7) - 1,即能存储的范围是[- (2^7) - 1, + (2^7) - 1]
故我们得到结论:有符号整型向上溢出(数值过大)会导致储存的数变为0,然后变为负数且逐渐减小;向下溢出(数值过小)则会变成0,然后逐渐增大
利用书本上的这张图可以很好地记住这一点:
图1. 有符号整数的溢出方式
2)无符号整数的溢出
与有符号整数相比,无符号整数无需存储正负号,故可用的位数总比有符号整数多一位,即储存范围是[0, +(2^8) - 1]
故我们得到结论:无符号整型如果向上溢出,会导致储存的值变为0,然后从0开始又逐渐增大;向下溢出,会导致储存的值变成能储存的最大值,然后逐渐变小
书本示意图如下所示:
图2. 无符号整数的溢出方式
C++内置整型大小清单
图源:https://www.runoob.com/cplusplus/cpp-data-types.html
3.1.5 选择整型类型
- 自然长度(natural size):计算机处理起来效率最高的长度,通常,int被设置为自然长度,所以如果没有其他十分必要的理由(例如很有必要节省内存等则用short),则应该首选整型int
- 使用short的场合:由于short比int小,故使用short更加节省内存,但是计算机在处理short时,也要先将其转为int再进行处理,故只对于提高计算效率而言,能用int表示的数字一般没必要用short来表示,但是当用于储存大型数组时,则有必要考虑用short,因为这样更加节省内存. 此外,在一些特殊情况下,例如要将程序从16位int的计算机移植到32位int的计算机上时,int会增大而影响精度,但使用short的话大小都相同,故可以用来避免类似的问题
3.1.6 整型的字面值
计算机如何表示各种进制
第一位数 | 第二位数 | 实例 | |
十进制 | 1-9 | 无要求 | 93 |
八进制 | 0 | 1-7 | 042 |
十六进制 | 0x 或 0X | 可能带有字母 a-f 或 A-F | 0xA5 |
八进制的读取方式
八进制 | 0 | 4 | 2 |
十进制 | 0*8^2 | 4*8^1 | 2*8^0 |
故:八进制 042 = 十进制 34
十六进制的读取方式
十六进制 | 0x | A | 5 |
十进制 | 无含义 | 10*16^1 | 5*16^0 |
a-f 或 A-F 表示了数字 10-15
故:十六进制 0xA5 = 十进制 165
C++中用于控制用何方式输出数值的命令
头文件iostream提供了:dec(十进制)、hex(十六进制)、oct(八进制)用于控制cout的输出方式,例如:
#include <iostream>
int main() {
int test = 42;
std::cout << std::dec; // 改成十进制
std::cout << "10: " << test << std::endl;
std::cout << std::hex; // 改成十六进制
std::cout << "16: " << test << std::endl;
std::cout << std::oct; // 改成八进制
std::cout << "8: " << test << std::endl;
}
结果:
10: 42
16: 2a
8: 52
3.1.7 C++如何确定常量的类型
除非有特殊情况,一般情况下C++默认整型为int类型
C++考察以下两点来判断常量类型:
1)后缀
可以使用后缀来表示数据所属类型,如图:
2)长度
C++选取能表示该数值的最小类型,如图:
** 注:十六进制在计算机中通常用来表示内存地址,因为这样的表示方式更节省空间
3.1.8 char类型:字符和小整数
计算机采用对字符编码的形式来处理字符,即在char中储存的是数字,在cout时才会被转换为字符进行输出,故可以对char进行整数操作,例如以下代码:
char aCh = 'A';
// 循环输出26个字母,及其相应的ASCII码
for (int i=0; i < 26; i++) {
std::cout << aCh << " " << (int)aCh << std::endl;
aCh += 1;
}
运行结果为:
A65 B66 C67 D68 E69 F70 G71 H72 I73 J74 K75 L76 M77 N78 O79 P80 Q81 R82 S83 T84 U85 V86 W87 X88 Y89 Z90
- 以英语为例,使用最广泛的是ASCII编码表,其中一部分如下图所示:
C++转义符:\
C++转义序列的编码:
whar_t (宽字符类型):支持拓展字符集,如日文汉字系统
wcin和wcout:用于处理 whar_t 流
** 注:cin和cout用于处理char流,故不适用于处理whar_t
C++11 新增类型:char16_t、char32_t,声明方法如下图所示:
3.1.9 bool类型
1)提升转换:bool转为int
示例如下:
bool b = true;
int a = b; // a = 1,true就是1
int c = false; // c = 0,false就是0
2)隐式转换:int转为bool
示例如下:
bool d = -100; // d = true,非零值都为true
bool e = 0; // e = false,零值都为false
3.2 const限定符
使用const限定符来表示常量是比#define更好的形式,const的命名方式有:
- 首字母大写:Name
- 全部大写:NAME
- 前缀加上k:kname
const变量需在一开始就初始化!举例如下:
const int MONTHS = 12; // 正确!
const int MONTHS;
MONTHS = 12; // 错误,这样的习惯不好!
3.3 浮点数
浮点数的储存方式
图3. 四字节 float 的储存方式
图4. 八字节 double 的储存方式
各个部分表示的含义:
- 符号位(sign):用于表示正负,其中0表示正,1表示负
- 指数位(exp):从右往左为2^0,2^1,2^2,......
- 尾数部分(Mantissa / frac):从左往右为2^(-1),2^(-2),2^(-3),......
图源:https://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
浮点数与整型相比的优缺点
浮点数的优点:
- 可以表示整数之间的值
- 由于有缩放因子,可以表示的范围比整数大得多
浮点数的缺点:
- 运算比整数慢
- 精度降低(例如float只能保证6个有效位数)
3.3.1 书写浮点数
1)普通方法
939001.32
1.0
8.0
0.005
2)科学计数法
2.52e+8
2.52e-8
2.52e8(等同于2.52e+8)
3.3.2 浮点类型
浮点类型有:float、double、long double.
如果不做特别声明,在C++中,一般浮点数(例如1.5或者2.0)将会被默认为double.
C++内置浮点类型一览
C++默认使用double类型来储存浮点数,也可以使用后缀声明类型,如图:
3.4 C++运算符
如图所示:
** 来源:https://www.runoob.com/cplusplus/cpp-operators.html
关于浮点数有效位数的问题
例如:11.17+50.25=61.42,但是C++会输出61.419998,这是因为float表示有效位数的能力有限,对于float,C++只能保证6位有效,即四舍五入后的61.4200为有效结果,这就是保证精度下的正确值.
3.4.1 运算符优先级及结合性
1)如果同时有乘除和加减,那么先计算乘除,后计算加减
2)如果只有乘除或加减,那么从左往右计算
3.4.2 除法分支
1)两个 int 相除,结果仍为 int,因为直接舍弃了小数部分(没有进行四舍五入操作)
2)两个float相除,结果为float
3)两个double相除,结果为double
4)两个不同类型的数据相除,C++自动转换为同一类型(下文将进行详细解说)
相同类型的除法如下图所示:
3.4.3 求模运算符(取余)
求模运算符(%)利用了整数除法(即两个数都为int的除法方式)
例如,计算 181 % 14 的步骤如下:
1)int类型的181 / 14 = 12
2)181 - (12*14) = 13,此时13即为余数
3.4.4 类型的转换
1. 初始化和赋值进行的转换
潜在的数值转换问题:
1)大的整形转化为小的整形,如int转换为short:
只读取右边的字节,例如一个大的整形含有2个字节(16bits):
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
如果要转换为小的整型,它只含有1个字节(8bits):
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
则小的整型只会读取右边较小的字节部分,即转换后结果为:
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
这样就超出了较小整型的取值范围,发生了数值损失.
2)大的浮点数转化为小的,如double转为float:
转化的原理同上,可能发生数值损失,同时浮点数的精度降低,不能保证与原数值相等.
3)浮点转化为整型,如float转换为int:
转化原理同上,但是int没有尾数部分,故小数部分会丢失;如果是double转化为int,还可能超出int的取值范围.
2. 以{}方式初始化时进行的转换
这种方式成为列表初始化,列表初始化不允许缩窄(narrowing),例如不允许浮点数转化为整型,举例:
char c1 { 31415 }; // 不允许,缩窄了
char c2 { 66 }; // 允许,没缩窄
char c3 { code }; // 允许
char c4 = { x }; // 不允许,此时x是变量而不是字符,且x没有被赋值
x = 31415 // 赋值给x
char c4 = x; // 缩窄了,但这种方式允许缩窄
3. 表达式中的转换
如果在表达式中,有两种不同的类型进行运算,则编译器通过校验表来确定在算术表达式中要进行的转换,C++对这个表稍作修改,下面是C++11版本的校验表:
对于上述整型级别的解释
有符号整型级别:long long > long > int > short > signed char
无符号整型类别同上,此外,char、unsigned char、signed char 级别相同,bool级别最低,whar_t、char16_t、char32_t 与其底层类型级别相同.
4. 传递参数时的转换(暂无实践经验,忽略)
5. 强制类型的转换
强制转换:不会改变原数据的类型,而是创建一个新的值,使得它可以在表达式中进行运算
1)通用格式
(typeName) value // 格式来自C语言
或
typeName(value) // 纯粹的C++
2)C++引入的4个强制类型转换符
static_cast<typeName> (value) // 该类型转换比传统转换更为严格
3.4.5 C++中的auto声明
举例如下:
auto n = 100; // int
auto x = 1.5; // double
auto y = 1.3e2L; // long double
有时候也不太如意,例如:
auto x = 0.0; // double
double y = 0; // double
auto z = 0; // int
当处理复杂类型时,auto的优势才能充分展现出来,如下:
std::vector<double> scores;
std::vector<double>::iterator pv = scores.begin();
可以重写为:
std::vector<double> scores;
auto pv = scores.begin();
Part B. 复习题
1)为什么C++有多种整型?
答:C++知道,没有任何一种整型和浮点型能满足所有的编程要求,因此对于这两种数据,它提供了多种变体
2)声明与下属描述相符的变量
答:
short整数,值为80
short int = 80;
unsigned整数,值为42110
unsigned int = 42110;
值为3*10^9的整数
long long num = 3.0E9;
3)C++提供了什么措施来防止超出整形范围?
答:C++不提供自动检测是否超出整型范围的功能,若用户输入的值超出所声明的整型的极限值,则会造成内存溢出,即出现内存储存的数据与输入数据不符的情况;但C++有提供多种不同存储容量的数据类型以满足不同的编程需求,例如short、int、long等,以及C++11推出的long long
4)33L与33之间有什么区别?
33L已经被声明为long int,而C++默认33是 int
5)下面两条C++语句是否等价?
char grade = 65;
char grade = 'A';
答:等价,因为使用cout输出char类型时,65会自动映射为字母A(根据ASCII码映射表)
6)如何使用C++来找出编码为88的字符?指出至少两种方法
第一种:
char ch = 88;
std::cout << ch << std::endl;
第二种:
char aCh = 'A';
// 循环输出26个字母,及其相应的ASCII码
for (int i=0; i < 26; i++) {
std::cout << aCh << " " << (int)aCh << std::endl;
aCh += 1;
}
结果:
A65 B66 C67 D68 E69 F70 G71 H72 I73 J74 K75 L76 M77 N78 O79 P80 Q81 R82 S83 T84 U85 V86 W87 X88 Y89 Z90
答案:X的编码为88
7)将long值赋给float变量会导致舍入误差,将long值赋值给double变量呢?将long long值赋值给double变量呢?
答:
实际代码测试:
#include <iostream>
#include <climits>
#include <iomanip> // 强制浮点数输出全部小数位
// 如果不导包,则float double都最多输出6位有效数字,则看不出精度差别
int main() {
long int l = LONG_MAX;
std::cout << std::setprecision(20); // 强制输出20位有效数字
std::cout << "long is : " << l << std::endl;
float f = l;
std::cout << "float for long_max is : " << f << std::endl;
double d = l;
std::cout << "double for long_max is : " << d << std::endl;
long long int ll = LONG_LONG_MAX;
std::cout << "long long is : " << ll << std::endl;
double dd = ll;
std::cout << "double for long_long_max is : " << dd << std::endl;
}
结果:
long is : 2147483647
float for long_max is : 2147483648
可见,long转为float损失最后一位精度
double for long_max is : 2147483647
long转为double精度未损失
long long is : 9223372036854775807
double for long_long_max is : 9223372036854775808
long long转为double精度损失一位
8)下列C++表达式的结果分别是多少?
代码:
#include <iostream>
int main() {
std::cout << 8*9+2 << std::endl; // int
std::cout << 6*3/4 << std::endl; // int
std::cout << 3/4*6 << std::endl; // int
std::cout << 6.0*3/4 << std::endl; // double
std::cout << 15%4 << std::endl; // int
}
结果:
74
4
0
4.5
3
9)假设x1和x2是两个double变量,要将他们作为整数相加,再将结果赋值给一个整型变量。请编写一条完成这项任务的C++语句?如果要将他们作为double值相加并转换为int呢?
答:
#include <iostream>
int main() {
double x1 = 1.0;
double x2 = 1.5;
int addInt = (int)x1 + (int)x2; // 整数相加
std::cout << "addInt: " << addInt << std::endl;
int addDouble = x1 + x2; // 相加后赋值给int
std::cout << "addDouble: " << addDouble << std::endl;
}
结果:
addInt: 2
addDouble: 2
结论:无论相加前还是相加后,转换为int时都是直接截掉小数部分
10)下面每条语句声明的变量都是什么类型?
auto cars = 15; // int
auto iou = 150.37f; // float
auto level = '8'; // char
auto crat = U'/U00002155'; // char32_t
auto fract = 8.25f/2.5; // float
Part C. 编程练习(暂停更新)
停更理由:最近比较忙,暂时停更
1)
代码:
#include <iostream>
const double FACTOR = 12.0; // 转换因子,需要设置为double,因此后面计算才显示小数
int main() {
int height;
std::cout << "Please enter your height (inch):_";
std::cin >> height;
std::cout << "your height is " << height/FACTOR << " foot" << std::endl;
}
测试结果:
Please enter your height (inch):_15
your height is 1.25 foot