C++ primer 第四章习题

chapter4 表达式

练习

4.1.2节练习

练习4.1

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

105

练习4.2

  • 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
(a) *vec.begin()            //*(vec.begin())
(b) *vec.begin() + 1        //(*(vec.begin())) + 1

4.1.3节练习

练习4.3

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

可以接受,为编译器和程序员留有了余地。

4.2节练习

练习4.4

  • 在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
12 / 3 * 4 + 5 *15 + 24 % 4 / 2
((((12 / 3) * 4) + (5 * 15)) + ((24 % 4) / 2 )) = 91

练习4.5

  • 写出下列表达式的求值结果。
(a) -30 * 3 + 21 / 5              (b) -30 + 3 * 21 / 5
(c) 30 / 3 * 21 % 5               (d) -30 / 3 * 21 % 4
//(a) : -86; (b) : -18; (c) : 0; (d) : -2;

练习4.6

  • 写出一条表达式用于确定一个整数是奇数还是偶数。
i % 2 == 0;

练习4.7

  • 溢出是何含义?写出三条将导致溢出的表达式。
//溢出是指计算的记过导致该数的值将超出数据类型所能表示的范围。
short s = 65536;
short s = -59999;
char c = 12345;

4.3节练习

练习4.8

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

逻辑与运算,优先计算左侧运算对象的值,仅当其为真时,才会计算右侧运算对象的值。

逻辑或运算,优先计算左侧运算对象的值,仅当其为假时,才会计算右侧运算对象的值。

相等性运算无顺序。

练习4.9

  • 解释在下面的if语句中条件部分的判断过程。
const cha *cp = "Hello World";
if (cp && *cp)
// 首先计算cp这个常量指针是否不为空指针,若为不为空指针,则解引用cp,查看其是否为空字符串。

练习4.10

  • 为while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。
int i;
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 的含义。

先判断j是否小于k,返回其bool值后提升为int整型1或0,再判断i是否为1或0。最后再返回一个bool值。

4.4节练习

练习4.13

  • 在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
int i; double d;
(a)  d = i = 3.5;                 (b) i = d = 3.5;
//(a) d = 3.0; i = 3;     (b) i = 3 ; d = 3.5;
 

练习4.14

  • 执行下述 if 语句后将发生什么情况?
if (42=i) //...      非法语句,编译器报错。赋值语句需左侧运算对象可修改。而42为字面值常量。
if (i=42) //...      若i非const变量,则恒为真,执行if内部语句。
 

练习4.15

  • 下面的赋值是非法的,为什么?应该如何修改?
double dval; int ival; int *pi;
dval = ival = pi = 0;
赋值运算符依照右结合律。故pi赋值0,得到空指针,却无法转化为int为ival赋值,所以语句非法。
将pi作为最左侧的运算对象进行赋值即可。如pi = dval = ival = 0;
 

练习4.16

  • 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
(a) if (p = getPtr() != 0)          (b) if (i = 1024)
(a) 由于赋值运算符的优先度较低,故getPtr()获得的值将先与0进行比较,判断是否为空指针。再将结果的bool值赋值给p。应为将p赋值的语句添加括号,提高优先级。改为 if ((p = getPtr()) != 0)
(b) 若i不是const变量,则将i赋值为1024后,i不为0,则作为if语句的判断条件将始终为真。若语句的本意是判断i是否为1024,则应修改为 if (i == 1024)
 

4.5节练习

练习4.17

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

前置递增运算符将首先将运算对象自增,再返回自增后的值。可作为左值。

后置递增运算符将首先将运算对象当前值的副本返回,再将运算对象自增。仅可作为右值,其返回对象仅是副本。

练习4.18

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

将返回自增加1之后的地址再被解引用。从vector中的第二个对象开始输出,也可能输出负值。输出最后一个元素时输出的是vector的尾后迭代器,导致溢出。

练习4.19

  • 假设 ptr 的类型是指向 int 的指针、vec的类型是vector、ival的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]
 
(a)首先判断ptr是否为空指针,若不为空指针,判断ptr指向的值是否为0。并将ptr指向下一个地址。
(b)判断ival和ival后的值是否不为零。
(c)c++未规定<=运算符的计算顺序,因此该表达式错误。应修改为vec[ival] <= vec[ival + 1]。比较在vec中第ival个数的值是否小于等于第ival + 1个数的值。
 

4.6节练习

练习4.20

  • 假设iter的类型是vector::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
(a) *iter++;                  //合法。返回iter指向的字符串,iter指向下一个字符串元素。
(b) (*iter)++;                //不合法,字符串没有自增运算。
(c) *iter.empty()             //不合法。点运算符优先级更高,而迭代器对象没有empty成员。
(d) iter->empty();            //合法。返回iter指向的字符串的empty成员。即判断其是否为空字符串。
(e) ++*iter;                  //不合法,字符串没有自增运算。
(f) iter++->empty();          //合法。判断iter指向的字符串是否为空,并将iter指向下一个字符串元素。
 

4.7节练习

练习4.21

  • 编写一段程序,使用条件运算符从 vector 中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> vec = { 1,2,3,4,5,6,7,8,9,10 };
    for (auto &i : vec){
        (i % 2 == 0) ? NULL : i *= 2 ;
    }
}
 

练习4.22

  • 本节的示例程序将成绩划分为high pass、pass和fail三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?
#include <string>
using namespace std;
int main()
{
    string finalgrade;
    int grade;
    finalgrade = (grade > 90) ? "high pass" : (grade > 75) ? "pass" : (grade < 60) ? "fail" : "low pass";
    if (grade > 90) finalgrade = "high pass";
    else if (grade > 75) finalgrade = "pass";
    else if (grade < 60) finalgrade = "fail";
    else finalgrade = "low pass";
}
 

if版本更易理解,其条件判断清晰,有分行帮助断句理解。条件运算符嵌套过后可读性降低,难以理解。

练习4.23

  • 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s";
+运算符优先级比==运算符优先级高,则与字符比较的将是字符串pl,而两者无法比较,故无法通过编译。
string pl = s + (s[s.size() - 1] == 's' ? "" : "s");
 

练习4.24

  • 本节的示例程序将成绩划分为 high pass、pass和fail三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?
    finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
 

优先判断grade > 90,根据返回值来执行"high pass"grade < 60,并将其作为最后一个条件运算符进行判断。最终返回的结果将是“fail”"pass",不会是"high pass",因为它被作为条件用于判断了。

4.8节练习

练习4.25

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

将先被提升为32位int,再进行左移操作值为00000000 00000000 00011100 01000000

练习4.26

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

因为unsigned int仅含16位,不足以以例子的方式存储30位学生的信息,会发生溢出或信息丢失等。

练习4.27

  • 下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2          //前29位均为0,后3位为011 即3。
(b) ul1 | ul2          //前29位均为0,后3位为111 即7。
(c) ul1 && ul2         //bool值 true
(d) ul1 || ul2         //bool值 true
 

4.9节练习

练习4.28

  • 编写一段程序,输出每一种内置类型所占空间的大小。
#include <iostream>
using namespace std;
int main()
{
    cout << sizeof(bool) << '\n';
    cout << sizeof(char) << '\n';
    cout << sizeof(wchar_t) << '\n';
    cout << sizeof(char16_t) << '\n';
    cout << sizeof(char32_t) << '\n';
    cout << sizeof(short) << '\n';
    cout << sizeof(int) << '\n';
    cout << sizeof(long) << '\n';
    cout << sizeof(long long) << '\n';
    cout << sizeof(float) << '\n';
    cout << sizeof(double) << '\n';
    cout << sizeof(long double) << '\n';
}
 

练习4.29

  • 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;    //输出数组长度10。对数组运算将得到整个数组所占空间大小。
cout << sizeof(p)/sizeof(*p) << endl;    //32位下为1,64位下为2。 因为指针的大小在32位机上是4字节,在64位上是8字节。而int为32位,4个字节。
 

练习4.30

  • 根据4.12节中的表(第147页),在下述表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同。
(a) sizeof x + y        // (sizeof x) + y
(b) sizeof p->mem[i]    // sizeof((p->mem)[i])
(c) sizeof a < b        // (sizeof a) < b
(d) sizeof f()          // sizeof (f())
 

4.10节练习

练习4.31

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

使用前置版本可以减少不必要的计算资源消耗。后置版本重写直接将运算符改为后置即可。

练习4.32

  • 解释下面这个循环的含义。
constexpr int size = 5;
int ia[size] = {1, 2, 3, 4, 5};
for (int *ptr = ia, ix = 0; ix != size && ptr != ia + size; ++ix, ++ptr)
{/* ... */}
 

遍历数组。

练习4.33

  • 根据4.12节中的表(第147页)说明下面这条表达式的含义。
someValue ? ++x, ++y : --x, --y;
逗号运算符将分隔表达式,形成 (someValue ? ++x, ++y : --x), --y;
若someValue 为真,自增x,舍弃x,自增y,舍弃y,自减y,返回y。
若someValue 为假,自减x,舍弃x,自减y,返回y。
 

4.11.1节练习

练习4.34

  • 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换:
  • 需要注意每种运算符遵循的是左结合律还是右结合律。
(a) if (fval)             //fval转化为bool。若fval为0,则转化为false,否则为true。
(b) dval = fval + ival    //ival转化为float,计算后的值转化为doubal赋值给dval。
(c) dval + ival * cval    //cval转化为int,计算后的值转为为double。
 

练习4.35

  • 假设有如下的定义,

    char cval;
    int ival;
    unsigned int ui;
    float fval;
    double dval;
    
    

    请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。

(a) cval = 'a' + 3;               //发生了,'a'转化为int,计算后的值转化为char
(b) fval = ui - ival * 1.0;       //发生了,ival转化为float。若float占的空间比unsigned int大,则ui转化为float。否则乘法计算后的值转化为unsigned int,最后转化为float。
(c) dval = ui * fval;             //发生了。若float占的空间比unsigned int大,则ui转化为float。否则fval转化为unsigned int,最后转化为double。
(d) cval = ival + fval + dval;    //ival,fval转化为double,计算后的结果转化为char
 

4.11.3节练习

练习4.36

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

练习4.37

  • 用命名的强制类型转换改写下列旧式的转换语句。
int i; double d; const string *ps; char *pc; void *pv;
(a) pv = (void*)ps;     改为 pv = static_cast<void*>(const_cast<string*>(ps));
(b) i = int(*pc);       改为 i = static_cast<int*>(pc);
(c) pv = &d;            改为 pv = static_cast<void*>(&d);
(d) pc = (char*) pv;    改为 pc = static_cast<char*>(pv);
 

练习4.38

  • 说明下面表达式的含义。
double slope = static_cast<double>(j / i);
 

将 j/i 的结果转换为 double,然后赋值给slope。

猜你喜欢

转载自blog.csdn.net/hwl19951007/article/details/89818308