练习 2.1
问题:类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?
答:
类型 | 大小 | 取值范围 |
---|---|---|
short | 2 字节即 16 位 | -2^15 ~ 2^15-1 |
int | 2 字节即 16 位 | -2^15 ~ 2^15-1 |
long | 4 字节即 32 位 | -2^31 ~ 2^31-1 |
long long | 8 字节即 64 位 | -2^63 ~ 2^63-1 |
带符号类型(signed)能够表示正数、负数和 0 ,而无符号类型(unsigned)只能够表示 0 和正数。
双精度浮点型(double)比单精度浮点型(float)的区别:① double 有效数字比 float 多,double 的内存占8个字节有效数字16位,而 float 的内存占4个字节有效数字8位;② 两者处理速度不同,float 的 CPU 速度比处理 double 快,而 double 的精度高导致消耗内存是 float 的两倍;③小数默认是 double 类型,而使用 float 时需要进行强转或者在小数后加上 f
练习 2.2
问题:计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。
答:实际应用中的利率、本金和付款既可能为整数,也可能为普通实数,因此可以选择一种浮点类型来表示。可选的浮点类型 float、double 和 long double,float 精度不足,没有必要使用 long double 的精度并且其 CPU 和内存的代价比较大。终上所述,采用 double 类型比较合适。
练习 2.3
问题:
答:
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl; // 32
std::cout << u - u2 << std::endl; // 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.4
问题:编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。
答:
std::cout << u - u2 << std::endl; // 4294967264
// 2^32 - (u-u2) = 4294967296 - 32 = 4294967264
练习 2.5
问题:指出下述字面值的数据类型并说明每一组内几种字面值的区别:
(a) 'a', L'a', "a", L"a"
(b) 10, 10u, 10L, 10uL, 012, 0xC
(c) 3.14, 3.14f, 3.14L
(d) 10, 10u, 10., 10e-2
答:
(a): 字符字面值,宽字符字面值,字符串字面值,宽字符串字面值。
(b): 十进制整型,十进制无符号整型,十进制长整型,十进制无符号长整型,八进制整型,十六进制整型。
(c): 双精度浮点型,单精度浮点型,扩展精度浮点型。
(d): 十进制整型,十进制无符号整型,双精度浮点型,双精度浮点型。
练习 2.6
问题:下面两组定义是否有区别,如果有,请叙述之:
int month = 9, day = 7;
int month = 09, day = 07;
答:有区别。第一行定义了两个十进制整型数,第二行定义了两个八进制整型数。但注意,就八进制而言,逢八进一,而不应出现第二行中的 09。
练习 2.7
问题:下述字面值表示何种含义?它们各自的数据类型是什么?
(a) "Who goes with F\145rgus?\012"
(b) 3.14e1L
(c) 1024f
(d) 3.14L
答:
(a) Who goes with Fergus?(换行),string 类型
(b) long double
(c) 无效,1024 是整型,而后缀 f 只能用于浮点字面量
(d) long double
练习 2.8
问题:请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。
答:
#include <iostream>
int main()
{
std::cout << 2 << "\115\012";
std::cout << 2 << "\t\115\012";
return 0;
}
练习 2.9
问题:解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。
(a) std::cin >> int input_value;
(b) int i = { 3.14 };
(c) double salary = wage = 9999.99;
(d) int i = 3.14;
答:
# (a) 应先定义好变量再用于接收标准输入。改正:
int input_value = 0;
std::cin >> input_value;
# (b) 用列表初始化内置类型的变量时,如果存在丢失信息的风险,则编译器将报错。改正:
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;
}
答:global_str 和 global_int 均为定义在函数体外的变量 (全局变量),故将被默认初始化为空字符串和 0。
local_int 作为函数体内的内置类型 (int) 变量 (局部变量),将不被初始化 (uninitialized),初值未定义。
local_str 作为函数体内的 std::string 类变量 (局部变量),其初值由该类决定,为空字符串。
练习 2.11
问题:指出下面的语句是声明还是定义:
(a) extern int ix = 1024;
(b) int iy;
(c) extern int iz;
答:
(a) 定义,虽然使用了 extern 关键字,但还是对变量 ix 进行初始化了,以至于抵消了 extern 的作用。
(b) 定义,未对变量 iy 初始化而已。
(c) 声明,使用了 extern 关键字且未对变量 iz 初始化。
练习 2.12
问题:请指出下面的名字中哪些是非法的?
(a) int double = 3.14;
(b) int _;
(c) int catch-22;
(d) int 1_or_2 = 1;
(e) double Double = 3.14;
答:
(a) 非法,变量名 double 使用了 C++ 关键字。
(b) 合法,但注意定义在函数体外的标识符不能以下划线开头。
(c) 非法,使用非法符号 (-) 构成标识符。
(d) 非法,标识符必须以字母或下划线开头。
(e) 合法,但注意变量名一般以小写字母开头,用户自定义类名才通常以大写字母开头。
练习 2.13
问题:下面程序中 j 的值是多少?
int i = 42;
int main()
{
int i = 100;
int j = i; // int j = ::i
}
答:局部变量 j = 100,因为新建的局部变量 i (100) 覆盖了原先的全局变量 i (42)。除非使用作用域操作符 :: 将局部变量 j 的创建和初始化语句改为 int j = ::i; 有 j = 42。
练习 2.14
问题:下面的程序合法吗?如果合法,它将输出什么?
int i = 100, sum = 0;
for (int i = 0; i != 10; ++i)
sum += i;
std::cout << i << " " << sum << std::endl;
答:该程序合法,因为 for 循环中的临时局部变量 i 将在内层作用域内暂时覆盖外层作用域的变量 i,从而实现正常的循环计数,并在 for 循环结束后被释放。结果将分别输出 100 45,其中 45 是整数 1-9 之和。
练习 2.15
问题:下面的哪个定义是不合法的?为什么?
(a) int ival = 1.01;
(b) int &rval1 = 1.01;
(c) int &rval2 = ival;
(d) int &rval3;
答:
(a) 合法,但浮点数 1.01 的小数部分会被截断 (隐式类型转换 double → int)。
(b) 非法,引用类型的初始值必须是一个对象。
(c) 合法,rval2 作为 int 型对象 ival 的引用,成为变量 ival 的别名。
(d) 非法,不能定义引用的引用,应当有初始化。
练习 2.16
问题:考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作?
int i = 0, &r1 = i;
double d = 0, &r2 = d;
(a) r2 = 3.14159;
(b) r2 = r1;
(c) i = r2;
(d) r1 = d;
答:
(a) 合法,等价于 d = 3.14159。
(b) 合法,等价于 d = 0.0,增加小数部分 (隐式类型转换 int → double)。
(c) 合法,等价于 i = 0,小数部分被截断 (隐式类型转换 double → int)。
(d) 合法,等价于 i = 0,小数部分被截断 (隐式类型转换 double → int)。
练习 2.17
问题:执行下面的代码段将输出什么结果?
int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl;
答:该代码段将输出 10 10
练习 2.18
问题:编写代码分别改变指针的值以及指针所指对象的值。
答:
#include<iostream>
int main()
{
int *ptr = nullptr;
std::cout << ptr << " " << std::endl; // 地址值
int x = 6;
ptr = &x; // int 指针 ptr 保存 int 变量 x 的内存地址 (ptr 指向 x)
std::cout << ptr << " " << *ptr << " " << x << std::endl; // 地址值、解引用值、变量 x
*ptr = 9; // ptr 解引用, 改变 ptr 所指地址中保存的值 (改变 x 的值)
std::cout << ptr << " " << *ptr << " " << x << std::endl; // 地址值、解引用值、变量 x
system("pause");
return 0;
}
#控制台输出:
#0
#0x61fe34 6 6
#0x61fe34 9 9
练习 2.19
问题:说明 指针和引用的主要区别。
答:指针和引用都能提供对其他对象的间接访问,且在定义时均需满足类型匹配原则。两者的区别:
- 引用本身不是一个对象即引用没有实际地址而是另一个对象的别名,故不能定义引用的引用;而指针本身就是一个对象即指针有实际地址,故可以定义指向指针的指针,也可以定义指针的引用。
- 引用在新建时必须绑定一个对象实现初始化,且引用一旦被定义就无法再绑定到其他对象;而指针及其保存的地址间则无此限制,指针无须在定义时赋初值,且在其生命周期内,可以通过赋值使之重新指向其他对象。
练习 2.20
问题:请叙述下面这段代码的作用。
int i = 42;
int *p1 = &i; // 等号右侧, & 为取地址符
*p1 = *p1 * *p1; // 等号右侧, * 为解引用符
答:
int i = 42; // 定义 int 型变量 i,并将 i 的值初始化为 42
int *p1 = &i; // 定义 int 型指针变量 p1,令指针 p1 指向对象 i
*p1 = *p1 * *p1; // 对指针变量 p1 解引用 从而为变量 i 赋予新值 1764
练习 2.21
问题:请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
int i = 0;
(a) double *dp = &i;
(b) int *ip = i;
(c) int *p = &i;
答:
(a) 非法。创建 double 型指针变量 dp,并令其指向 int 型对象 i,导致类型不一致。
(b) 非法。把 int 变量 i 直接赋给指针 ip 是错误的操作,即便 i 的值恰为 0 也不行。
(c) 合法。创建 int 型指针变量 p,并令其指向 int 型对象 i ,即 p 保存变量 i 的内存地址。
练习 2.22
问题:假设 p 是一个 int 型指针,请说明下述代码的含义。
if (p) // ...
if (*p) // ...
答:Line 1:判断 p 是否为空指针。
Line 2:判断 p 所指对象的值 (整数) 是否为 0。
练习 2.23
问题:给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。
答:不能,因为首先要确定指针 p 是否合法,才能判断 p 所指向的对象是否合法。
练习 2.24
问题:在下面这段代码中为什么 p 合法而 lp 非法?
int i = 42;
void *p = &i;
long *lp = &i;
答:因为指针变量 lp 的类型为 long,与指向对象 i 的类型 int 不一致。注意,在 C++ 中,继承自 C 的 void * 才可以指向任何类型的对象,而其他指针类型必须要与所指对象严格匹配。
练习 2.25
问题:说明下列变量的类型和值。
(a) int* ip, i, &r = i;
(b) int i, *ip = 0;
(c) int* ip, ip2;
答:
(a) ip 是指向 int 对象的指针;i 是 int 型变量;r 是对变量 i 的 int 型引用
(b) i 是 int 型变量;ip 是 int 型的空指针
(c) ip 是指向 int 对象的指针;ip2 是 int 型变量
练习 2.39
问题:编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。
struct Foo { /* 此处为空 */ } // 注意:没有分号
int main()
{
return 0;
}
答:在 struct Foo ... } 处提示应输入“ ; ” 。
练习 2.40
问题:根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。
答:
struct Sales_data {
std::string book_name; // 书名
std::string book_index; // 书编号
unsigned sales_volume = 0; // 销售总量
double unit_price = 0.0; // 书单价
double unit_cost = 0.0; // 单书成本
double total_revenue = 0.0; // 总营收
double net_profit = 0.0; // 净利润
// ...
};
练习 2.41
问题:使用你自己的 Sale_data 类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把 Sales_data 类的定义和 main 函数放在一个文件里。
答:
#include<iostream>
#include<string>
struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
Sale_data book;
double price;
std::cin >> book.bookNo >> book.units_sold >> price;
book.revenue = book.units_sold * price;
std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;
system("pause");
return 0;
}
练习 2.42
问题:根据你自己的理解重写一个 Sales_data.h 头文件,并以此为基础重做 2.6.2节(第67页)的练习。
答: