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

练习4.1

表达式 5 + 10 * 20 / 2 的求值结果是多少?

      运算符优先级:乘法和除法 > 加法和减法 > 逻辑运算符,如果运算符优先级相同,按照从左到右顺序运算。

      求值结果为105。

练习4.2

根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。

      括号是无视优先级和结合律

* vec.begin()                // *(vec.begin())
* vec.begin() + 1            // (*(vec.begin())) + 1

练习4.3

C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。

      关键还是在于编程者对于程序的编写,虽然有时,我们需要编译器的特性来高效工作。但在处理复合表达式时,最好使用括号强制让表达式的组合关系符合逻辑要求,而不要让编译器做这种行为不可预知的操作。

练习4.4

在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。

12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
// 加上括号
((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2)
// 结果是91

练习4.5

写出下列表达式的求值结果。

-30 * 3 + 21 / 5             // -90+4 = -86
-30 + 3 * 21 / 5             // -30+63/5 = -30+12 = -18
30 / 3 * 21 % 5              // 10*21%5 = 210%5 = 0
-30 / 3 * 21 % 4             // -10*21%4 = -210%4 = -2

练习4.6

写出一条表达式用于确定一个整数是奇数还是偶数。

      判断能否被2整除即可。

i % 2 == 0 ? "even" : "odd"

练习4.7

溢出是何含义?写出三条将导致溢出的表达式。

      当计算的结果超出了该类型所能表示的最大范围时就会产生溢出。

short svalue = 32767; ++svalue;              // -32768
unsigned uivalue = 0; --uivalue;             // 4294967295
unsigned short usvalue = 65535; ++usvalue;   // 0

练习4.8

说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。

逻辑与:当且仅当左边为真时,才计算第二个操作数。

逻辑或:当且仅当左边为假时,才计算第二个操作数。

相等运算符:仅当两个操作数具有相同的值时才为真,否则返回false。

练习4.9

解释在下面的if语句中条件部分的判断过程。

const char *cp = "Hello World";
if (cp && *cp)

cp是指向const char *的指针,它不是nullptr。true

cp是一个const char: 'H',它显式地表示一个非零值。true

true && true => true

练习4.10

为while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。

int i = 0;
while(cin >> i && i != 42)

练习4.11

书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。

a > b && b > c && c > d

练习4.12

假设i、j 和k 是三个整数,说明表达式 i != j < k 的含义。

      根据操作符优先级,i != j < k 等价于 i != (j < k)。即表达式 j < k 的结果(true或者false)与 i 进行比较,该表达式的bool结果是!=操作符的右侧运算对象。

练习4.13

在下述语句中,当赋值完成后 i 和 d 的值分别是多少?

int i;   double d;

d = i = 3.5;           // i = 3, d = 3.0
i = d = 3.5;           // d = 3.5, i = 3

练习4.14

执行下述 if 语句后将发生什么情况?

if (42 = i)   // 编译错误:表达式不可赋值
if (i = 42)   // 赋值运算作为条件,i不为0,结果true

练习4.15

下面的赋值是非法的,为什么?应该如何修改?

double dval; int ival; int *pi;
dval = ival = pi = 0;

      pi是一个指向int的指针,这里把"int *"赋值给"int"类型,是不可以的,应该改为:

dval = ival = 0;
pi = 0;

练习4.16

尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?

if (p = getPtr() != 0)
if (i = 1024)

// 这里赋值语句被当做条件,应该改成
if ((p = getPtr()) != 0)
if (i == 1024)

练习4.17

说明前置递增运算符和后置递增运算符的区别。

      前置递增运算符:将对象本身作为一个值返回。

      后置递增运算符:将对象的原始值的副本作为一个值返回。

练习4.18

如果132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?

      程序会从第二个元素开始输出,直到最后一个元素的后一个位置,最后一个元素的后一个位置是未定义的,结果未知。

练习4.19

假设 ptr 的类型是指向 int 的指针、vec 的类型是vector、ival 的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?

(a) ptr != 0 && *ptr++  
(b) ival++ && ival
(c) vec[ival++] <= vec[ival] 

      c++中明确规定了逻辑与(&&)、逻辑或(||)、条件运算符(?:)、逗号运算符(,)这四种运算符的求值顺序,对于其他的运算符都没有规定运算对象的求值顺序。

ptr != 0 && *ptr++            // 首先检查ptr是不是空指针,如果不是再检查ptr指向的值是不是0
ival++ && ival                // 检查ival和ival+1是否等于0
vec[ival++] <= vec[ival]      // 不正确,<=操作符没有规定运算对象的求值顺序,编译器有可能先求左边也有可能先求右边。
                              // 左侧的运算对象改变了ival的值,所以该语句是未定义的

练习4.20

假设 iter 的类型是 vector::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?

*iter++;                // 合法,iter先加1,再返回iter初始值的副本并解引用得到它指向的值
(*iter)++;              // 不合法, iter解引用后是一个string,字符串没有增量
*iter.empty()           // 不合法, iter是一个指针,没有empty()成员,不能用.操作符,应该用->
iter->empty();          // 合法,判断iter指向的值是否为空
++*iter;                // 不合法, iter解引用得到string,但是字符串没有增量
iter++->empty();        // 合法,判断iter所指向的值是否为空,再对iter加1

练习4.21

编写一段程序,使用条件运算符从 vector 中找到哪些元素的值是奇数,然后将这些奇数值翻倍。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (auto& i : ivec) {
        i = (i % 2) ? (i * 2) : i;
    }

    for (auto i : ivec) std::cout << i << " ";
    std::cout << std::endl;

    return 0;
}

练习4.22

本节的示例程序将成绩划分为high pass、pass 和 fial 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?

#include <iostream>

using std::cout;
using std::cin;
using std::endl;

int main()
{
    unsigned grade;
    while (cin >> grade) {
        // 第一种,只使用条件运算符
        cout << ((grade > 90) ? "high pass" : (grade < 60)
                                                  ? "fail"
                                                  : (grade < 75) ? "low pass"
                                                                 : "pass");
        cout << endl;
        // 第二种,使用if语句
        if (grade > 90)
            cout << "high pass";
        else if (grade < 60)
            cout << "fail";
        else if (grade < 75)
            cout << "low pass";
        else
            cout << "pass";
        cout << endl;
    }

    return 0;
}

练习4.23

因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?

      条件运算符的优先级很低,嵌套使用时最好加上括号。

string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;

练习4.24

本节的示例程序将成绩划分为 high pass、pass、和fail三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?

finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";

// 如果条件运算符满足的是左结合律,等同于下面这样
finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";
// 如果成绩为> 90,则第一个条件运算符的结果为high pass,所以期末成绩总是不及格,很明显这是错的。

练习4.25

如果一台机器上 int 占 32 位、char 占8位,用的是 Latin-1 字符集,其中字符’q’ 的二进制形式是 01110001,那么表达式’q’ << 6的值是什么?

      这里首先char类型的对象提升成int类型,对象原来的位保持不变,往高位添加0,增加24个高位0。位取反运算符的优先级要高于左移运算符,之后按位取反得到的结果是一个负数。位运算符如何处理运算对象的符号位依赖于机器,而且这里的左移操作可能会改变符号位的值,因此是一种未定义行为。

练习4.26

在本节关于测验成绩的例子中,如果使用unsigned int 作为quiz1 的类型会发生什么情况?

      本节的例子中有30个学生,所以使用的类型最少要有32位,无符号长整型在任何机器上至少有32位,因此可以确保够用。但是标准定义无符号整型最少能容纳16位,如果采用无符号整型,则结果是未定义的。

练习4.27

下列表达式的结果是什么?

unsigned long ul1 = 3, ul2 = 7;
ul1 & ul2                  // 结果为 3
ul1 | ul2                  // 结果为 7
ul1 && ul2                 // 结果为 true
ul1 || ul2                 // 结果为 ture

练习4.28

编写一段程序,输出每一种内置类型所占空间的大小。

#include <iostream>

using std::cout;
using std::endl;

int main()
{
    cout << "bool\t\tis " << sizeof(bool) << "bytes." << endl;
    cout << "char\t\tis " << sizeof(char) << "bytes." << endl;
    cout << "wchar_t\t\tis " << sizeof(wchar_t) << "bytes." << endl;
    cout << "char16_t\tis " << sizeof(char16_t) << "bytes." << endl;
    cout << "char32_t\tis " << sizeof(char32_t) << "bytes." << endl;
    cout << "short\t\tis " << sizeof(short) << "bytes." << endl;
    cout << "int\t\tis " << sizeof(int) << "bytes." << endl;
    cout << "long\t\tis " << sizeof(long) << "bytes." << endl;
    cout << "long long\tis " << sizeof(long long) << "bytes." << endl;
    cout << "float\t\tis " << sizeof(float) << "bytes." << endl;
    cout << "double\t\tis " << sizeof(double) << "bytes." << endl;
    cout << "long double\tis " << sizeof(long double) << "bytes." << endl;
    cout << endl;

    return 0;
}

// Print
/**************************/
// bool            is 1bytes.
// char            is 1bytes.
// wchar_t         is 2bytes.
// char16_t        is 2bytes.
// char32_t        is 4bytes.
// short           is 2bytes.
// int             is 4bytes.
// long            is 4bytes.
// long long       is 8bytes.
// float           is 4bytes.
// double          is 8bytes.
// long double     is 16bytes.

练习4.29

推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?

int x[10];   int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;       // 输出10,sizeof对数组使用,得到整个数组所占空间的大小
                                            // 除以每个元素所占大小后,输出的是x中的元素数量
cout << sizeof(p)/sizeof(*p) << endl;       // 由于指针的大小在不同的机器上是不同的,所以结果取决于机器,可能是2或是1

练习4.30

根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。

sizeof x + y      // (sizeof x)+y,"sizeof"比"+"具有更高的优先级
sizeof p->mem[i]  // sizeof(p->mem[i])
sizeof a < b      // sizeof(a) < b
sizeof f()        // 不需要改变,如果"f()"返回"void",则此语句未定义,否则将返回返回类型的大小。

练习4.31

本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。

      书上在4.5节说过了,除非在必要时使用后置版的递增递减运算符,否则优先使用前置版的,这样是一个好习惯。原因很简单:前缀版避免了不必要的工作。使用后置版重写这一节的程序和原程序没什么区别:

for(vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--)
    ivec[ix] = cnt;

练习4.32

解释下面这个循环的含义。

      ptr和ix具有相同的功能,都可以循环遍历数组。前者使用指针,后者使用数组的索引。

练习4.33

根据4.12节中的表说明下面这条表达式的含义。

someValue ? ++x, ++y : --x, --y

(someValue ? ++x, ++y : --x), --y         // 逗号运算符的优先级最低,等价于这个表达式
                                          // 如果someValue为真,则++x,结果为y,如果someValue为假,则--x,结果为--y

练习4.34

根据本节给出的变量定义,说明在下面的表达式中奖发生什么样的类型转换:

if (fval)               // fval的float转换成bool
dval = fval + ival;     // ival的int转换成float, 然后将fval加ival的结果转换为double
dval + ival * cval;     // cval的char转换成int,然后那个cval和ival相乘的结果转换成double。

练习4.35

假设有如下的定义:

char cval;
int ival;
unsigned int ui;
float fval;
double dval;
请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。

(a) cval = 'a' + 3;               // 'a'提升为int,然后('a' + 3)(int)的结果转换为char。
(b) fval = ui - ival * 1.0;       // ival转换为double, ui也转换为double。然后将double强制类型转换为float
(c) dval = ui * fval;             // ui提升为float,然后这个float转换为double。
(d) cval = ival + fval + dval;    // ival转换为float,得到ival和fval的和,然后ival和fval的和转换为double
                                  // 最后,double被强制类型转换成char

练习4.36

假设 i 是int类型,d 是double类型,书写表达式 i*=d 使其执行整数类型的乘法而非浮点类型的乘法。

i *= static_cast<int>(d);

练习3.37

用命名的强制类型转换改写下列旧式的转换语句。

int i; double d; const string *ps; char *pc; void *pv;

pv = (void*)ps;         // pv = const_cast<string*>(ps);或者 pv = static_cast<void*>(const_cast<string*>(ps));
i = int(*pc);           // i = static_cast<int>(*pc);
pv = &d;                // pv = static_cast<void*>(&d);
pc = (char*)pv;         // pc = reinterpret_cast<char*>(pv);

练习3.38

说明下面这条表达式的含义。

double slope = static_cast<double>(j/i);

      j/i结果是int,转换成double并赋值给slope。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值