第二章 变量和基本类型

练习2.1

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

解:

C++ 规定 short 和 int 至少16位,long 至少32位,long long 至少64位。 带符号类型能够表示正数、负数和 0 ,而无符号类型只能够表示 0 和正整数。

练习2.2

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

解:

使用double。需要进行浮点计算。

练习2.3

读程序写结果。

  1. unsigned u = 10, u2 = 42;
  2. std::cout << u2 - u << std::endl;
  3. std::cout << u - u2 << std::endl;
  4. int i = 10, i2 = 42;
  5. std::cout << i2 - i << std::endl;
  6. std::cout << i - i2 << std::endl;
  7. std::cout << i - u << std::endl;
  8. std::cout << u - i << std::endl;

解:

输出:

  1. 32
  2. 4294967264
  3. 32
  4. -32
  5. 0
  6. 0

练习2.4

编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。

练习2.5

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

  1. (a) 'a', L'a', "a", L"a"
  2. (b) 10, 10u, 10L, 10uL, 012, 0xC
  3. (c) 3.14, 3.14f, 3.14L
  4. (d) 10, 10u, 10., 10e-2

解:

  • (a): 字符字面值,宽字符字面值,字符串字面值,宽字符串字面值。
  • (b): 十进制整型,十进制无符号整型,十进制长整型,十进制无符号长整型, 八进制整型,十六进制整型。
  • (c): double, float, long double
  • (d): 十进制整型,十进制无符号整型,double, double

练习2.6

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

  1. int month = 9, day = 7;
  2. int month = 09, day = 07;

解:

第一行定义的是十进制的整型,第二行定义的是八进制的整型。但是month变量有误,八进制不能直接写9。

练习2.7

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

  1. (a) "Who goes with F\145rgus?\012"
  2. (b) 3.14e1L
  3. (c) 1024f
  4. (d) 3.14L

解:

  • (a) Who goes with Fergus?(换行),string 类型
  • (b) long double
  • (c) 无效,因为后缀f只能用于浮点字面量,而1024是整型。
  • (d) long double

练习2.8

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

解:

  1. #include <iostream>
  2. int main()
  3. {
  4. std::cout << 2 << "\115\012";
  5. std::cout << 2 << "\t\115\012";
  6. return 0;
  7. }

练习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): 应该先定义再使用。

  1. int input_value = 0;
  2. std::cin >> input_value;

(b): 用列表初始化内置类型的变量时,如果存在丢失信息的风险,则编译器将报错。

  1. double i = { 3.14 };

(c): 在这里wage是未定义的,应该在此之前将其定义。

  1. double wage;
  2. double salary = wage = 9999.99;

(d): 不报错,但是小数部分会被截断。

  1. double i = 3.14;

练习2.10

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

  1. std::string global_str;
  2. int global_int;
  3. int main()
  4. {
  5. int local_int;
  6. std::string local_str;
  7. }

解:

global_strglobal_int是全局变量,所以初值分别为空字符串和0。
local_int是局部变量并且没有初始化,它的初值是未定义的。
local_strstring 类的对象,它的值由类确定,为空字符串。

练习2.11

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

  • (a) extern int ix = 1024;
  • (b) int iy;
  • (c) extern int iz;

解:

(a): 定义
(b): 定义
(c): 声明

练习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), (c), (d) 非法。

练习2.13

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

  1. int i = 42;
  2. int main()
  3. {
  4. int i = 100;
  5. int j = i;
  6. }

解:

j的值是100,局部变量i覆盖了全局变量i

练习2.14

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

  1. int i = 100, sum = 0;
  2. for (int i = 0; i != 10; ++i)
  3. sum += i;
  4. 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;

解:

(b)和(d)不合法,(b)引用必须绑定在对象上,(d)引用必须初始化。

练习2.16

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

  1. int i = 0, &r1 = i;
  2. double d = 0, &r2 = d;
  • (a) r2 = 3.14159;
  • (b) r2 = r1;
  • (c) i = r2;
  • (d) r1 = d;

解:

  • (a): 合法。给 d 赋值为 3.14159。
  • (b): 合法。会执行自动转换(int->double)。
  • (c): 合法。会发生小数截取。
  • (d): 合法。会发生小数截取。

练习2.17

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

  1. int i, &ri = i;
  2. i = 5; ri = 10;
  3. std::cout << i << " " << ri << std::endl;

解:

输出:10, 10

练习2.18

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

解:

  1. int a = 0, b = 1;
  2. int *p1 = &a, *p2 = p1;
  3. // change the value of a pointer.
  4. p1 = &b;
  5. // change the value to which the pointer points
  6. *p2 = b;

练习2.19

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

解:

引用是另一个对象的别名,而指针本身就是一个对象。
引用必须初始化,并且一旦定义了引用就无法再绑定到其他对象。而指针无须在定义时赋初值,也可以重新赋值让其指向其他对象。

练习2.20

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

  1. int i = 42;
  2. int *p1 = &i;
  3. *p1 = *p1 * *p1;

解:

让指针 pi 指向 i,然后将 i 的值重新赋值为 42 * 42 (1764)。

练习2.21

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

int i = 0;

  • (a) double* dp = &i;
  • (b) int *ip = i;
  • (c) int *p = &i;

解:

  • (a): 非法。不能将一个指向 double 的指针指向 int
  • (b): 非法。不能将 int 变量赋给指针。
  • (c): 合法。

练习2.22

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

  1. if (p) // ...
  2. if (*p) // ...

解:

第一句判断 p 是不是一个空指针,
第二句判断 p 所指向的对象的值是不是为0

练习2.23

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

解:

不能,因为首先要确定这个指针是不是合法的,才能判断它所指向的对象是不是合法的。

练习2.24

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

  1. int i = 42;
  2. void *p = &i;
  3. long *lp = &i;

解:

void *是从C语言那里继承过来的,可以指向任何类型的对象。
而其他指针类型必须要与所指对象严格匹配。

练习2.25

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

  1. (a) int* ip, i, &r = i;
  2. (b) int i, *ip = 0;
  3. (c) int* ip, ip2;

解:

  • (a): ip 是一个指向 int 的指针, i 是一个 int, r 是 i 的引用。
  • (b): i 是 int , ip 是一个空指针。
  • (c): ip 是一个指向 int 的指针, ip2 是一个 int。

练习2.26

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

解:

  1. const int buf; // 不合法, const 对象必须初始化
  2. int cnt = 0; // 合法
  3. const int sz = cnt; // 合法
  4. ++cnt; ++sz; // 不合法, const 对象不能被改变

练习2.27

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

解:

  1. int i = -1, &r = 0; // 不合法, r 必须引用一个对象
  2. int *const p2 = &i2; // 合法,常量指针
  3. const int i = -1, &r = 0; // 合法
  4. const int *const p3 = &i2; // 合法
  5. const int *p1 = &i2; // 合法
  6. const int &const r2; // 不合法, r2 是引用,引用没有顶层 const
  7. const int i2 = i, &r = i; // 合法

练习2.28

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

解:

  1. int i, *const cp; // 不合法, const 指针必须初始化
  2. int *p1, *const p2; // 不合法, const 指针必须初始化
  3. const int ic, &r = ic; // 不合法, const int 必须初始化
  4. const int *const p3; // 不合法, const 指针必须初始化
  5. const int *p; // 合法. 一个指针,指向 const int

练习2.29

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

解:

  1. i = ic; // 合法, 常量赋值给普通变量
  2. p1 = p3; // 不合法, p3 是const指针不能赋值给普通指针
  3. p1 = &ic; // 不合法, 普通指针不能指向常量
  4. p3 = &ic; // 合法, p3 是常量指针且指向常量
  5. p2 = p1; // 合法, 可以将普通指针赋值给常量指针
  6. ic = *p3; // 合法, 对 p3 取值后是一个 int 然后赋值给 ic

练习2.30

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

  1. const int v2 = 0; int v1 = v2;
  2. int *p1 = &v1, &r1 = v1;
  3. const int *p2 = &v2, *const p3 = &i, &r2 = v2;

解:

v2 是顶层const,p2 是底层const,p3 既是顶层const又是底层const,r2 是底层const。

练习2.31

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

解:

  1. r1 = v2; // 合法, 顶层const在拷贝时不受影响
  2. p1 = p2; // 不合法, p2 是底层const,如果要拷贝必须要求 p1 也是底层const
  3. p2 = p1; // 合法, int* 可以转换成const int*
  4. p1 = p3; // 不合法, p3 是一个底层const,p1 不是
  5. p2 = p3; // 合法, p2 和 p3 都是底层const,拷贝时忽略掉顶层const

练习2.32

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

  1. int null = 0, *p = null;

解:

合法。指针可以初始化为 0 表示为空指针。

练习2.33

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

解:

  1. a=42; // a 是 int
  2. b=42; // b 是一个 int,(ci的顶层const在拷贝时被忽略掉了)
  3. c=42; // c 也是一个int
  4. d=42; // d 是一个 int *,所以语句非法
  5. e=42; // e 是一个 const int *, 所以语句非法
  6. g=42; // g 是一个 const int 的引用,引用都是底层const,所以不能被赋值

练习2.34

基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?如果不对,请反复研读本节的示例直到你明白错在何处为止。

练习2.35

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

  1. const int i = 42;
  2. auto j = i; const auto &k = i; auto *p = &i;
  3. const auto j2 = i, &k2 = i;

解:

j 是 int,k 是 const int的引用,p 是const int *,j2 是const int,k2 是 const int 的引用。

练习2.36

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

  1. int a = 3, b = 4;
  2. decltype(a) c = a;
  3. decltype((b)) d = a;
  4. ++c;
  5. ++d;

解:

c 是 int 类型,值为 4。d 是 int & 类型,绑定到 a,a 的值为 4 。

练习2.37

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

  1. int a = 3, b = 4;
  2. decltype(a) c = a;
  3. decltype(a = b) d = a;

解:

c 是 int 类型,值为 3。d 是 int& 类型,绑定到 a。

练习2.38

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

解:

decltype 处理顶层const和引用的方式与 auto不同,decltype会将顶层const和引用保留起来。

  1. int i = 0, &r = i;
  2. //相同
  3. auto a = i;
  4. decltype(i) b = i;
  5. //不同 d 是一个 int&
  6. auto c = r;
  7. decltype(r) d = r;

练习2.39

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

  1. struct Foo { /* 此处为空 */ } // 注意:没有分号
  2. int main()
  3. {
  4. return 0;
  5. }。

解:

提示应输入分号。

练习2.40

根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。

  1. struct Sale_data
  2. {
  3. std::string bookNo;
  4. std::string bookName;
  5. unsigned units_sold = 0;
  6. double revenue = 0.0;
  7. double price = 0.0;
  8. //...
  9. }

练习2.41

使用你自己的Sale_data类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把Sales_data类的定义和main函数放在一个文件里。

  1. // 1.5.1
  2. #include <iostream>
  3. #include <string>
  4. struct Sale_data
  5. {
  6. std::string bookNo;
  7. unsigned units_sold = 0;
  8. double revenue = 0.0;
  9. };
  10. int main()
  11. {
  12. Sale_data book;
  13. double price;
  14. std::cin >> book.bookNo >> book.units_sold >> price;
  15. book.revenue = book.units_sold * price;
  16. std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;
  17. return 0;
  18. }
  1. // 1.5.2
  2. #include <iostream>
  3. #include <string>
  4. struct Sale_data
  5. {
  6. std::string bookNo;
  7. unsigned units_sold = 0;
  8. double revenue = 0.0;
  9. };
  10. int main()
  11. {
  12. Sale_data book1, book2;
  13. double price1, price2;
  14. std::cin >> book1.bookNo >> book1.units_sold >> price1;
  15. std::cin >> book2.bookNo >> book2.units_sold >> price2;
  16. book1.revenue = book1.units_sold * price1;
  17. book2.revenue = book2.units_sold * price2;
  18. if (book1.bookNo == book2.bookNo)
  19. {
  20. unsigned totalCnt = book1.units_sold + book2.units_sold;
  21. double totalRevenue = book1.revenue + book2.revenue;
  22. std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
  23. if (totalCnt != 0)
  24. std::cout << totalRevenue / totalCnt << std::endl;
  25. else
  26. std::cout << "(no sales)" << std::endl;
  27. return 0;
  28. }
  29. else
  30. {
  31. std::cerr << "Data must refer to same ISBN" << std::endl;
  32. return -1; // indicate failure
  33. }
  34. }
  1. // 1.6
  2. #include <iostream>
  3. #include <string>
  4. struct Sale_data
  5. {
  6. std::string bookNo;
  7. unsigned units_sold = 0;
  8. double revenue = 0.0;
  9. };
  10. int main()
  11. {
  12. Sale_data total;
  13. double totalPrice;
  14. if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
  15. {
  16. total.revenue = total.units_sold * totalPrice;
  17. Sale_data trans;
  18. double transPrice;
  19. while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
  20. {
  21. trans.revenue = trans.units_sold * transPrice;
  22. if (total.bookNo == trans.bookNo)
  23. {
  24. total.units_sold += trans.units_sold;
  25. total.revenue += trans.revenue;
  26. }
  27. else
  28. {
  29. std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
  30. if (total.units_sold != 0)
  31. std::cout << total.revenue / total.units_sold << std::endl;
  32. else
  33. std::cout << "(no sales)" << std::endl;
  34. total.bookNo = trans.bookNo;
  35. total.units_sold = trans.units_sold;
  36. total.revenue = trans.revenue;
  37. }
  38. }
  39. std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
  40. if (total.units_sold != 0)
  41. std::cout << total.revenue / total.units_sold << std::endl;
  42. else
  43. std::cout << "(no sales)" << std::endl;
  44. return 0;
  45. }
  46. else
  47. {
  48. std::cerr << "No data?!" << std::endl;
  49. return -1; // indicate failure
  50. }
  51. }

练习2.42

根据你自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页)的练习。