【C++ Primer Plus 第六版 学习笔记】第三章 处理数据 复习题 答案 解析 超详细(更新完毕)

本文介绍了C++中的变量命名规则、整型和浮点型的概念、溢出原理、数据类型转换以及运算符的使用。讲解了不同整型变量的范围、浮点数的储存方式和精度问题,并探讨了类型转换和自动类型推断(auto)的作用。此外,还涉及了常量的声明、浮点数的表示以及运算符的优先级和结合性。
摘要由CSDN通过智能技术生成

Part A. 学习笔记

本章内容:

  1. 使用变量的方法
  2. 在C++中进行算术运算
  3. 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,当使用二进制时,示意图如下:

00001111

这个字节储存的数字为15,原因如下图所示:

二进制00001111
十进制0*2^80*2^60*2^50*2^41*2^31*2^21*2^11*2^0

将十进制读取后的数字相加,即可得到数字15

3. 整型溢出原理

1)有符号整数的溢出

补码原理:符号整数表示负数的方式

假设一个数字为3,它在一个字节中的的储存形式为:

00000011

将它的0全部变成1,1全部变成0,再加上1,则有:

11111101

直接对以上两者做二进制加法,我们得到:

00000000

本来应该为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
八进制01-7042
十六进制0x 或 0X可能带有字母 a-f 或 A-F0xA5

八进制的读取方式

八进制042
十进制0*8^24*8^12*8^0

故:八进制 042 = 十进制 34

十六进制的读取方式

十六进制0xA5
十进制无含义10*16^15*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的命名方式有:

  1. 首字母大写:Name
  2. 全部大写:NAME
  3. 前缀加上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

浮点数与整型相比的优缺点

浮点数的优点

  1. 可以表示整数之间的值
  2. 由于有缩放因子,可以表示的范围比整数大得多

浮点数的缺点

  1. 运算比整数慢
  2. 精度降低(例如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):

1000000000000001

如果要转换为小的整型,它只含有1个字节(8bits):

00000000

则小的整型只会读取右边较小的字节部分,即转换后结果为:

00000001

这样就超出了较小整型的取值范围,发生了数值损失.

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

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值