C++Primer第五版 第二章习题答案

  • Post author:
  • Post category:其他


练习2.1

类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?

short和int至少16位,long至少16位,long long至少32位。

有符号可以表示正数、负数和零,无符号只能表示不小于零的数。

float是单精度浮点数,有6位有效数字,double是双精度浮点数,有10位有效数字。

用法:

使用整数运算。short通常太小,实际上long通常与int的大小相同,如果数据值大于int的最小保证大小,那么就使用long long。(简而言之:short < int < long < long long)

当知道值不能为负时,使用无符号类型。(一句话:没有否定,没有署名。)

使用双精度浮点计算;float通常没有足够的精度,而且双精度计算与单精度计算的成本可以忽略不计。事实上,在某些机器上,双精度操作比单精度操作要快。long double所提供的精度通常是不必要的,而且常常需要相当大的运行时成本。(简而言之:float < double < long double)

练习2.2

计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。

使用double或者float。

练习2.3

读程序写结果。


当一个算数表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。

unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;        // 结果为32
std::cout << u - u2 << std::endl;        // 结果为4294967264,因为10-42应该是-32,在计算机中用二进制存储
                                         // 第一位是1表示负数,但由于是无符号型,二进制第一位变成0,即为4294967264
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;        // 结果为32
std::cout << i - i2 << std::endl;        // 结果为-32
std::cout << i - u << std::endl;         // 结果为0
std::cout << u - i << std::endl;         // 结果为0

练习2.5

指出下述字面值的数据类型并说明每一组内几种字面值的区别:

前缀: u                  unicode16字符                     char16_t

U                 unicode32字符                     char32_t

L                  宽字符                                  wchar_t

u8                utf-8                                      char

后缀:u或U            表示该字面值的最小匹配类型为无符号类型

l或L              表示该字面值的最小匹配类型为long

ll或LL           表示该字面值的最小匹配类型为long long

f或F             表示该字面值最小匹配类型为float类型

l或L              表示该字面值最小匹配类型为long double

(a) 'a', L'a', "a", L"a"              // 字符、宽字符、字符串、字符串宽字符
(b) 10, 10u, 10L, 10uL, 012, 0xC*     // 十进制、无符号十进制、长十进制、无符号长十进制、八进制、十六进制
(c) 3.14, 3.14f, 3.14L                // double、float、long double
(d) 10, 10u, 10., 10e-2               // 十进制、无符号十进制、double、double

练习2.6

下面两组定义是否有区别,如果有,请叙述之:

第一行的整数定义的是十进制的数

第二行的定义是不合法的,因为0开头的是八进制,但是八进制中是没有9的。

练习2.7

下述字面值表示何种含义?它们各自的数据类型是什么?

(a): Who goes with Fergus?(new line)      // string

(b): 31.4                                 // long double

(c): 1024                                 // float

(d): 3.14                                 // long double

练习2.8

请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。

#include <iostream>

int main()
{
    std::cout << "\062\115\012";
    std::cout << "\062\t\115\012";
}

练习2.9

解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。

(a): 不合法,不能在输入时才定义,需在cin之前定义input_value。

int input_value = 0;
std::cin >> input_value;    

(b):不合法,当我们使用列表初始化且初始值存在丢失信息的风险,编译器会报错,这里类型“double”不能缩小为“int”。

double i = { 3.14 };

(c):不合法,未声明wage就使用。

double wage;
double salary = wage = 9999.99;

(d):合法,但是值会被强制类型转换。

double i = 3.14;

练习2.10

下列变量的初值分别是什么?

std::string global_str;

int global_int;

int main()

{


int local_int;

std::string local_str;

}


1、定义在任何函数体外的变量会被初始化为0。


2、在函数体中定义的内置类型的未初始化对象具有未定义的值。没有显式初始化的类类型的对象有一个类定义的值。

global_str是全局变量,所以值是空字符串。

global_int是全局变量,所以值为0。

local_int是一个未初始化的局部变量,所以它有一个未定义的值。

local_str也是一个未初始化的局部变量,但是它有一个类定义的值。它是空字符串。

练习2.11

指出下面的语句是声明还是定义:

(a) extern int ix = 1024;         // 定义
(b) int iy;                       // 定义
(c) extern int iz;                // 声明

练习2.12

请指出下面的名字中哪些是非法的?


C++的标识符由字母、数字和下划线组成,其中必须以字母或下划线开头。同时,用户自定义的标识符不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。

(a) int double = 3.14;            // 不合法,使用了关键字double作为变量名
(b) int _;                        // 合法
(c) int catch-22;                 // 不合法,“-”无效字符
(d) int 1_or_2 = 1;               // 不合法,不能以数字开头
(e) double Double = 3.14;         // 合法

练习2.13

下面程序中 j 的值是多少?

int i = 42;

int main()

{


int i = 100;

int j = i;

}



新建局部变量会覆盖同名的全局变量,所以j的值为100。

练习2.14

下面的程序合法吗?如果合法,它将输出什么?

int i = 100, sum = 0;

for (int i = 0; i != 10; ++i)

sum += i;

std::cout << i << ” ” << sum << std::endl;

合法,输出:100 45

练习2.15

下面的哪个定义是不合法的?为什么?


引用是为对象起的另外一个名字,定义引用时,程序把引用和它的初始值绑定在一起,而不是把初始值拷贝给引用,并且无法令引用重新绑定到另外一个对象,所以引用必须初始化。

(a) int ival = 1.01;              // 合法
(b) int &rval1 = 1.01;            // 不合法,初始化必须是一个对象
(c) int &rval2 = ival;            // 合法
(d) int &rval3;                   // 不合法,引用必须初始化

练习2.16

考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作?

(a): 合法 令d = 3.14159

(b): 合法 强制类型转换

(c): 合法 强制类型转换

(d): 合法 强制类型转换

练习2.17

执行下面的代码段将输出什么结果?

int i, &ri = i;

i = 5; ri = 10;

std::cout << i << ” ” << ri << std::endl;

输出:10 10

练习2.18

编写代码分别改变指针的值以及指针所指对象的值。

int a = 0, b = 1;
int *p1 = &a, *p2 = p1;

// 改变指针的值
p1 = &b;
// 改变指针所指对象的值
*p2 = b;

练习2.19

说明指针和引用的主要区别

定义:指针是一个任何其他类型的“指向”,而引用是对象的“另一个名称”。

区别:

  • 引用是已存在对象的另一个名称。指针本身就是一个对象。
  • 一旦初始化,引用就绑定到它的初始对象上,无法重新绑定引用以引用不同的对象。而指针可以重新分配或拷贝。
  • 引用必须初始化。指针在定义时可以不需要初始化。
  • 引用不能为NULL,指针可以为NULL。

练习2.20

请叙述下面这段代码的作用。

int i = 42;

int *p1 = &i;

*p1 = *p1 * *p1;

p1指针指向i, i的值改为1764(42*42)

练习2.21

请解释下述定义。在这些定义中有非法的吗?如果有,为什么?

(a) double* dp = &i;         // 不合法,不能用int *类型的值初始化double *类型的变量
(b) int *ip = i;             // 不合法,不能用一个类型为int的值初始化一个类型为int *的变量
(c) int *p = &i;             // 合法

练习2.22

假设 p 是一个 int 型指针,请说明下述代码的含义。

if (p)       // 是否p是一个空指针
if (*p)      // p所指向的值是否是0

练习2.23

给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。

不可以,因为需要更多的信息来确定指针是否有效。

练习2.24

在下面这段代码中为什么 p 合法而 lp 非法?

int i = 42;

void *p = &i;

long *lp = &i;

因为void*类型的指针可以保存任何对象的地址。但是不能用int *类型的对象初始化long *类型的变量。

练习2.25

说明下列变量的类型和值。

(a) int* ip, i, &r = i;    // ip是指向int的指针,i是一个整数,r是i的引用。
(b) int i, *ip = 0;        // ip是一个空指针,i是一个整数。
(c) int* ip, ip2;          // ip是一个指向int的指针,ip2是一个整数。

练习2.26

下面哪些语句是合法的?如果不合法,请说明为什么?


有关const的用法总结,我在另一篇博客

C++中Const的用法总结

中写了。

const int buf;         // 不合法, const对象必须要初始化
int cnt = 0;           // 合法
const int sz = cnt;    // 合法
++cnt;                 // 合法
++sz;                  // 不合法, sz是一个const对象,不能被改变

练习2.27

下面的哪些初始化是合法的?请说明原因。

int i = -1, &r = 0;         // 不合法, r必须引用一个对象,const int才可以引用常量
int *const p2 = &i2;        // 合法,p2为一个常量指针,初始化为i2对象的地址
const int i = -1, &r = 0;   // 合法,i是一个常量,r是一个常量引用
const int *const p3 = &i2;  // 合法,p3是一个指向常量对象的常量的引用
const int *p1 = &i2;        // 合法,p1是一个指向常量对象的指针
const int &const r2;        // 不合法, r2是一个引用,不能const
const int i2 = i, &r = i;   // 合法,i2是一个常量,r是一个常量引用,不能够通过r修改i的值

练习2.28

说明下面的这些定义是什么意思,挑出其中不合法的。

int i, *const cp;           // 不合法,常量指针cp必须初始化
const int ic, &r = ic;      // 不合法,ic必须初始化
int *p1, *const p2;         // 不合法,常量指针p2必须初始化
const int *const p3;        // 不合法,常量指针p3必须初始化
const int *p;               // 合法,p是一个指向常量对象的指针

练习2.29

假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。

i = ic;                     // 合法
p1 = p3;                    // 不合法, p3是一个指向常量对象的指针
p1 = &ic;                   // 不合法, ic是一个const int,p1是一个int *
p3 = &ic;                   // 不合法, p3是一个常量指针
p2 = p1;                    // 不合法, p2是一个常量指针
ic = *p3;                   // 不合法, ic是一个const int.

练习2.30

对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?

const int v2 = 0; int v1 = v2;

int *p1 = &v1, &r1 = v1;

const int *p2 = &v2, *const p3 = &i, &r2 = v2;

v2是顶层const, p2是底层const。

p3:最右边是顶层const,最左边是底层const。

r2是底层const。

练习2.31

假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。

r1 = v2;                    // 合法,v2的顶层const可以被忽略
p1 = p2;                    // 不合法,p2有一个const,但是p1没有
p2 = p1;                    // 合法,可以从int *转换成const int *.
p1 = p3;                    // 不合法,p3有底层const,但是p1没有
p2 = p3;                    // 合法,p2具有与p3相同的底层const

练习2.32

下面的代码是否合法?如果非法,请设法将其修改正确。

int null = 0, *p = null;

不合法

int null = 0, *p = nullptr;

练习2.33

利用本节定义的变量,判断下列语句的运行结果。


auto会自动忽略掉顶层const,而底层const会被保留。


auto定义的变量必须有初始值。

a = 42;                     // 把42赋给int a.
b = 42;                     // 把42赋给int b.
c = 42;                     // 把42赋给int c.
d = 42;                     // 错误, d是一个int *,应该是:*d = 42;
e = 42;                     // 错误, e是一个const int *,应该是:e = &c;
g = 42;                     // 错误, g是一个绑定到ci上的const int& 

练习2.35

判断下列定义推断出的类型是什么,然后编写程序进行验证。

const int i = 42;                 // i是const int.
auto j = i;                       // j是int
const auto &k = i;                // k是const int&
auto *p = &i;                     // ps是const int *
const auto j2 = i, &k2 = i;       // j2是const int,k2是const int&

练习2.36

关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。


decltype((variable))双层括号的结果永远是引用,decltype(variable)单层括号的结果只有当variable本身就是一个引用时才是引用。

int a = 3, b = 4;
decltype(a) c = a;                // c是int类型
decltype((b)) d = a;              // d是a的引用
++c;                              // c的值是4
++d;                              // d的值是4

练习2.37

赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。


当decltype()的括号中是一个表达式时,如果表达式的结果对象能作为一条赋值语句的左值,将返回一个引用类型。

int a = 3, b = 4;
decltype(a) c = a;                // c是一个int类型
decltype(a = b) d = a;            // d是一个a的引用,这里编译器只分析表达式的类型,并不实际执行表达式,所以a的值不是4
// 值: a = 3, b = 4, c = 3, d = 3

练习2.38

说明由decltype 指定类型和由auto指定类型有何区别。请举一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。



如果使用引用类型,auto会识别为其所指对象的类型,decltype则会识别为引用的类型。

decltype的结果类型与表达式形式密切相关。

int i = 0, &r = i;
// 相同
auto a = i;
decltype(i) b = i;
// 不相同
auto c = r;
decltype(r) d = r;

练习2.39

编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。

struct Foo { /* 此处为空  */ } // 注意:没有分号

int main()

{


return 0;

}

会报错 Error message:

[Error] expected ';' after struct definition



版权声明:本文为Sakuya__原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。