C++Primer_课后习题第二章

本门答案,部分参考于C++ Primer 习题集

第一章答案在这里:https://blog.csdn.net/Huangpengyu123/article/details/106605401

开头先放一张自己写的导图

在这里插入图片描述

2.1

① int,long,long long ,short 都属于整形,区别是C++标准规定的的尺寸的最小值,就是可以表示的最大的尺寸不同.

名字 长度 大小
Short 短整型 16
Int 整形 16
long 长整型 32
long long 长整型 64

无符号的类型可以表示的数比有符号类型的数大.

Float和Double分别是单精度浮点数和双精度浮点数.

2.2

选择Double比较适合

Float精度不够,而且也省多少空间.

2.3+2.4

代码如下:

#include<iostream>

int main(void) {
	unsigned  u = 10, u2 = 42;
	std::cout << u2 - u << std::endl;
	std::cout << u - u2 << std::endl;
	int i = 10, i2 = 42;
	std::cout << i2 - i << std::endl;
	std::cout << i - i2 << std::endl;
	std::cout << i - u << std::endl;
	std::cout << u - i << std::endl;
	return 0;
}

结果如下:

32
4294967264
32
-32
0
0

那个第二个结果是因为在我的环境里面,一个int是占32位的,最大是

4,294,967,295

官方资料如下:

在这里插入图片描述

https://docs.microsoft.com/zh-cn/cpp/cpp/data-type-ranges?view=vs-2019

地址.

其他都是好理解的.这里过.

如果有不理解的,可以评论问我

2.5

(a)

‘a’ 普通字符

L’a’ 宽字符型字面值,类型是wchar_t

“a” 普通字符串

L"a" 宽字符型字符串

(b)

10 int整形

10u unsigned

10L long

10uL unsigned long

012 8进制的数

0xC 16进制数

©

3.14 Double型

3.14f float型

3.14L long double 型

(d)

10 int

10u unsigned int

  1.                       double
    

10e-2 科学计数法 表示为1
10102 10*10^{-2}

2.6

不一样的,第一组定义的是10进制数.

第二组定义的是8进制数

而且 第二组的定义有错误,第二组定义了

int month=09;

而8进制最大而只有数字7

定义都是错误的

2.7

代码如下,我写了一个输出的代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main(void) {
	string a;
	a = "Who goes with F\145rgus?\012";
	cout << a<<endl;
	cout << 3.14e1L << endl;
	//cout << 1024f;		//在我的VS2019中,编译会报错.
	cout << 3.14L << endl;
	return 0;
}

结果如下:

Who goes with Fergus?

31.4
3.14

(a)

是一个字符串,里面套了转义字符.

我们知道,没有加\x的转义字符,后面默认的就是8进制

所以\145就是1*8*8+4*8+5=101 在Ascii码表里面这个就是小写字母e

然后\012 就是1*8+2=10 表示换行.

(b)

3.14e1L 是一个科学计数法的长Double型
3.14101 3.14*10^{1}

©

这个在我的编译器里会报错

要把1024f

改为1024.f

就是设置为浮点数

(d)

3.14L

就是设置为长double

2.8

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main(void) {
	string a="";
	a = "2M\n";
	cout << a;
	a = "2\tM\n";
	cout << a;
	return 0;
}

结果如下:

2M
2       M

2.9

(a)

错.

输入运算符的右侧需要一个明确的变量名称,而不是定义变量的语句

int input_value;
	std::cin >> input_value;

(b)

这个是列表初始化,列表初始化不允许有初始值存在丢失信息的风险.编译器会报错

改正之后

int i = { 3 };

©

声明语句中声明多个变量时需要用逗号将变量名隔开,而不能使用赋值运算符连接.

改正后

#include<iostream>
#include<string>

int main(void) {
	double salary, wage;
	salary = wage = 9999.99;

	return 0;
}

(d)

会报一个警告,但是不会报错.

改正后

	int i = 3;

2.10

Global_int 是 0

local_int 在我的编译器里面不能编译.

其他的str都是0

2.11

(a)

定义了变量ix

(b)

声明并定义了变量iy

©

声明了变量iz

2.12

b 和 e是合法的

其他都是非法的

(a) double是C++里面的关键字

© -不可以作为变量名

(d) 变量名开头一定要是字母

2.13

j=100

因为

int j=i;	//这里的i是main函数里面的i=100

2.14

合法

输出100,45

代码如下:

#include<iostream>
#include<string>
std::string global_str;
int global_int;
int main(void) {
	int i = 100, sum = 0;
	for (int i = 0; i != 10; ++i) {
		sum += i;
	}
	std::cout << i << " " << sum << std::endl;
	return 0;
}

输出100是因为i用的main函数里面的 i 而不是for里面的i

sum的值就是一个0–9的累加.

2.15

(a)

会有个警告,但是不会非法的

(b)

非法的

引用必须引用在一个对象上,而不能引用在某个表达式的值上,和字面值上。

©

合法

(d)

引用必须初始化,

2.16

(a)

合法的,实际上,就是把3.14159赋值给了d

(b)

合法,实际上,就是把i的值赋值给了d

©

合法,实际就是把d赋值给了i

进行了窄化操作

(d)

合法,实际就是把d赋值给了i

进行了窄化操作

2.17

完整可运行代码如下:

#include<iostream>
#include<string>
int main(void) {
	int i, & ri = i;
	i = 5, ri = 10;
	std::cout << i << " " << ri << std::endl;
	return 0;
}

结果是

10 10

因为引用就相当于一个别名,还是改变了i的值

2.18

测试代码如下:

#include<iostream>
#include<string>
int main(void) {
	int i = 10, * p = &i,b=5;
	//改变指针指向对象的值
	std::cout << "P的值是:" << p << std::endl;
	std::cout << "i的值是:" <<i << std::endl;
	*p = 15;
	std::cout << "P的值是:" << p << std::endl;
	std::cout << "i的值是:" << i << std::endl;
	//改变指针的值
	p = &b;
	std::cout << "P的值是:" << p << std::endl;
	return 0;
}

结果如下:

P的值是:004FFAA4
i的值是:10
P的值是:004FFAA4
i的值是:15
P的值是:004FFA8C

2.19

这题的答案参考于C++Prime的习题集(主要是他写的蛮好的,就借鉴了哈)

指针"指向"内存中的某个对象,而引用"绑定到"内存中的某个对象,它们都实现了对其他对象的间接访问,二者的区别主要有两方面:

① 指针本身就是一个对象,允许对指针的赋值和拷贝,而且在指针的生命周期内它可以指向几个不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象.

② 指针无须再定义时赋初始值,和其他内置类型一样,再块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值;引用必须再定义的时候赋初值.

2.20

#include<iostream>
#include<string>
int main(void) {
	int i = 42;
	int* p1 = &i;
	*p1 = *p1 * *p1;	//*p1就代表i,相当于i=i*i
	std::cout << i << std::endl;
	return 0;
}

这段代码的意思,就是通过指针变量p1指向i

在通过p1来改变i的值.

2.21

(a)

非法

类型不匹配

double 类型的指针,不能指向int类型的变量

(b)

非法

不匹配,在指针指向的过程,需要&取地址符

©

合法

2.22+2.23

if§ //指的是判断p是不是一个无效指针,有没有指向任意一个对象.

if(*p) //指的是判断p所指向的那个对象的指等于不等于0

下面是测试的代码:

#include<iostream>
#include<string>
int main(void) {
	int i = 42;
	int* p1 = &i, * p2 = 0, * p3 = nullptr;
	if (p1)
		std::cout << "p1 is not null";
	else
		std::cout << "p1 is null";

	std::cout << "\n";

	if (p2)
		std::cout << "p2 is not null";
	else
		std::cout << "p2 is null";

	std::cout << "\n";

	if (p3)
		std::cout << "p3 is not null";
	else
		std::cout << "p3 is null";

	std::cout << "\n";

	return 0;
}

结果如下:

p1 is not null
p2 is null
p3 is null

2.24

void 类型的指针可以存放任意类型的地址.

long类型和int类型不匹配

2.25

(a)

int* ip, i, & r = i;

*ip是一个int类型的指针

​ 它如果是一个全局变量,那么它就是为空

​ 如果它在快作用域里面,它的值就是不确定的

i 就是一个普通的int类型的变量

​ 全局为空

​ 快作用域不确定

r是i的一个引用

​ r跟着i走走,i是什么,它是什么.

(b)

int i, * ip = 0;

i 就是一个普通的int类型的变量

​ 全局为空

​ 快作用域不确定

ip 是一个int类型的空指针

©

int* ip, ip2;

ip2 就是一个普通的int类型的变量

​ 全局为空

​ 快作用域不确定

*ip是一个int类型的指针

​ 它如果是一个全局变量,那么它就是为空

​ 如果它在快作用域里面,它的值就是不确定的

2.26

(a)

非法的

const类型必须初始化

(b)

合法的

©

合法的,可以用int类型来初始化const int对象

(d)

非法的

++cnt是合法的

++sz是非法的

sz是const类型,const类型不能改变它的值.

2.27

(a)

是非法的,非常亮引用 r 不能引用字面值常量0

(b)

是合法的,p2是一个常量指针,p2的值永不改变,即p2永远指向变量i2

©

是合法的,i是一个常量,r是一个常量引用,此时r可以绑定倒字面值常量0中

(d)

是合法的,p3是一个常量指针,p3的值永不改变,即p3永远指向变量i2,同时p3指向的是常量,即我们不能通过p3改变所指对象的值.

(e)

是合法的,p1指向一个常量,即我们不能通过p1改变所指对象的值

(f)

是非法的,引用本身不是对象,因此不能让引用恒定不变

(g)

是合法的,i2是一个常量,r是一个常量引用

2.28

(a)

int *const cp

const 指针必须初始化

(b)

int *p1,*const p2

const指针必须初始化

©

const 常量必须要初始化 ic

(d)

const int *const p3;		

p3是一个常量指针,指向的对象是一个int类型的常量

常量指针必须要初始化,所以错误

不合法

(e)

合法,但是p没有指向任何实际的对象

2.29

(a)

合法的

(b)

非法的

普通指针p1指向了一个常量

而且,普通指针p1的值可以任意的改变,这与实际不符

©

非法

普通指针p1指向了一个常量

普通指针p1的值可以任意的改变,这与实际不符

(d)

非法

p3只能再定义的时候,被初始化,后来就不可以改变p3的指向了

(e)

非法的

p2是一个常量指针,只能再定义的时候,被初始化,后来就不可以改变p2的指向了

(f)

非法的

ic只能再定义的时候,被初始化

2.30

v2和p3是顶层const,分别表示一个整形常量和一个整形常量指针

p2和r2是底层const,分别表示它们所指(所引用的对象是常量)

2.31

r1 = v2;	//r1是一个普通引用,可以通过v2来改变r1的值,实际上,就相当与把v2的值,赋值给了v1
p1 = p2;	//非法的,p1是一个普通指针,p2是一个指向常量的指针
	//p2是一个指向常量的指针,p1=p2的指向,会引起冲突
p2 = p1;	//合法的,p1是一个普通指针,p2是一个指向常量的指针
	//p2是一个指向常量的指针
	//p2不会改变p1指向的对象的内容
p1 = p3;	//合法的,p1是一个普通指针
	//p3是一个常量指针,指向的是一个常量对象
	//p1=p3
	//p1可能会修改p3指向的对象的值,会引起冲突
p3 = p1;	//合法的,p1是一个普通指针
	//p3是一个常量指针,指向的是一个常量对象
	//p3=p1
	//p3不可能修改p1指向的对象的值,不会引起冲突

2.32

错误

指针不可以直接绑定一个int类型

根据题意,可以修改为

int null = 0, * p = nullptr;

2.33

前三条语句都是正确的,因为a,b,c都是整数

后三条都不太正确.

d,e都是指针,这样肯定是错误的.

g是一个整型常量引用,他是不可以改变引用的对象的.

2.34

#include<iostream>
#include<string>
int main(void) {
	int i = 0, & r = i;				//一个普通整形,和一个普通整形引用
	auto a = r;						//这个a是int型的(r是i的别名,i还是一个整数)
	const int ci = i, & cr = ci;	//这个ci就是整形常量,cr是整形常量引用
	auto b = ci;					//b还是一个整数(ci的顶层const属性被忽视掉了--因为ci是一个常量,所以它拥有顶层const属性)
	auto c = cr;					//c还是一个整数(cr是ci的别名),ci的顶层const属性一样被忽略掉了
	auto d = &i;					//d是一个整形指针,好理解
	auto e = &ci;					//e是一个指向整数常量的指针(对常量对象取地址是一种底层的const)
									//对指针来讲,对常量取地址就是底层的const
	const auto f = ci;				//ci推出来还是int类型,但是f是const int就是整形常量
	auto& g = ci;					//g是一个整形常量引用,绑定到ci
									//auto类型在进行引用的时候,对象的顶层属性依然被保留

	std::cout << a << ":" << b << ":" << c << ":" << d << ":" << e << ":" << g << std::endl;

	//auto& h = 42;					//引用不能绑定常量
	const auto& j = 42;				//f是一个整形常量引用,可以绑定到字面值	

	auto k = ci, & l = i;			//k是一个int,l是一个整形引用	这里auto的类型是int型
	auto& m = ci, * p = &ci;		//m是一个整形常量引用,p是一个指向整型常量的指针	这里auto的类型是const int型
	//auto& n = i, * p2 = &ci;		//这里的话,前面的&n是一个int类型的引用,
									//后面的*p2又是一个const int 类型的指针,两个类型冲突,所以报错
	a = 42;		//a是int类型,不影响r,和i
	std::cout << r << ":" << i << std::endl;
	b = 42;		//b也是同理
	std::cout << cr << ":" << ci << std::endl;
	c = 42;		//c也是同理
	std::cout << cr << ":" << ci << std::endl;

	//d = 42;
	//e = 42;
	//g = 42;
	std::cout << a << ":" << b << ":" << c << ":" << d << ":" << e << ":" << g << std::endl;

	return 0;
}

2.35

这个是我自己写的验证程序

自己写的

#include<iostream>
#include<string>
int main(void) {
	const int i = 42;				//整形常量
	auto j = i;						//j就是一个普通的int类型
	const auto& k = i;				//一个整形常量引用
	auto* p = &i;					//一个整形常量指针
	const auto j2 = i, & k2 = i;	//整形常量j2,整形常量引用k2
	//i = 43;						//报错
	j = 10;							//可以修改
	//k = 10;						//对const的引用,不允许通过k来修改i的值
	int a = 10;
	p = &a;							//这样是可以的,而且a的值,自己还可以改变
	//*p = 42;						//p是一个指向常量的指针,不允许通过p来修改它指向的对象
									//但是p指向的对象可以自己修改自己的值PS,如果它可以的话
	a = 42;
	//j2 = 10;						//报错
	//k2 = 10;						//报错
	return 0;
}

参考答案

#include<iostream>
#include<typeinfo>
int main(void) {
	const int i = 42;
	auto j = i;
	const auto& k = i;
	auto* p = &i;
	const auto j2 = i, & k2 = i;
	std::cout << typeid(i).name() << std::endl;
	std::cout << typeid(j).name() << std::endl;
	std::cout << typeid(k).name() << std::endl;
	std::cout << typeid(p).name() << std::endl;
	std::cout << typeid(j2).name() << std::endl;
	std::cout << typeid(k2).name() << std::endl;
	return 0;
}

运行结果如下:

我的环境是VS2019

int
int
int
int const *
int
int

2.36

#include<iostream>
#include<string>
int main(void) {
	int a = 3, b = 4;
	decltype(a) c = a;		//int 类型的c
	decltype((b)) d = a;	//d是a的引用.
	++c;	
	++d;					//++d相对于++a
	std::cout << a << ":" << b << ":" << c << ":" << d << std::endl;
	return 0;
}

2.37

这题我套用了上一题的代码

#include<iostream>
#include<string>
int main(void) {
	int a = 3, b = 4;
	decltype(a) c = a;		//int 类型的c
	decltype(a=b) d = a;	//d是a的引用.因为(a=b)这个整体是一个表达式,decltype里面对表达式的类型
							//判断就是为引用.
	++c;	
	++d;					//++d相对于++a
	std::cout << a << ":" << b << ":" << c << ":" << d << std::endl;
	return 0;
}

2.38

这题答案,是我参考的

① auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,但是不计算表达式的值

② 编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当的改变结果类型使其更符合初始化的规则

例如:auto一般会忽略顶层const,而把底层的const保留下来,与之相反,decltype会保留变量的顶层const

③ 与auto不同,decltype的结果类型与表达式形式密切相关,如果变量名加了一对括号,则得到的类型与不加括号时会有不同.如果deltype使用的是一个不加括号的变量,则得到的结果就是改变量的类型;如果给变量加上了一层或多层括号,则编译器将推断得到引用类型

下面是一个例子

#include<iostream>
#include<typeinfo>
int main(void) {
	int a = 3;					//int
	auto c1 = a;				//int
	decltype(a) c2 = a;			//int
	decltype((a)) c3 = a;		//int 引用
	
	const int d = 5;			//const int
	auto f1 = d;				//int 
	decltype(d) f2 = d;			//整型常量

	std::cout << typeid(c1).name() << std::endl;
	std::cout << typeid(c2).name() << std::endl;
	std::cout << typeid(c3).name() << std::endl;
	std::cout << typeid(f1).name() << std::endl;
	std::cout << typeid(f2).name() << std::endl;

	c1++;
	c2++;
	c3++;						//因为c3是a的引用,实际就相当与自增a了
	f1++;
	//f2++;						//因为是整形常量,所以不能自增
	std::cout << a << ":" << c1 << ":" << c2 << ":" << c3 << ":" << f1 << ":" << f2 << std::endl;
	return 0;
}

2.39

在这里插入图片描述

2.40

struct Sales_data {
	std::string bookNo;			//书籍编号
	unsigned units_sold = 0;	//销售量
	double sellingprice = 0.0;	//零售价
	double saleprice = 0.0;		//实售价
	double discount = 0.0;		//折扣
};

2.41

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.42

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果这篇文章对你有张帮助的话,可以用你高贵的小手给我点一个免费的赞吗

相信我,你也能变成光.

在这里插入图片描述

如果你有任何建议,或者是发现了我的错误,欢迎评论留言指出.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章