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

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

如果這篇文章對你有張幫助的話,可以用你高貴的小手給我點一個免費的贊嗎

相信我,你也能變成光.

在這裏插入圖片描述

如果你有任何建議,或者是發現了我的錯誤,歡迎評論留言指出.

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