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

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

105

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

4.4 求下列表达式的值。

    int result = 12 / 3 * 4 + 5 * 15 +24 % 4 / 2;
    cout << result << endl;                                  // 91

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

    int result1 = -30 * 3 + 21 / 5; // -86
    int result2 = -30 + 3 * 21 / 5; // -18
    int result3 = 30 / 3 * 21 % 5; // 0
    int result4 = -30 / 3 * 21 % 4; // -2

4.6 写出一个表达式,确定一个整数是奇数还是偶数。

    int num = 4;
    num % 2 == 1 ? cout << "odd" << endl : cout << "even" << endl;

4.7 溢出是何含义,写出三条会溢出的表达式。

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

short c = (short)65538; // print c = 2, overflow

4.8 说明在逻辑与、逻辑或及相等运算符中运算对象求值的顺序。
相等运算符先求值,逻辑与、逻辑或运算优先级相同,按照从左到右的顺序求值。

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

const char *cp = "Hello World";
if (cp && *cp); 
 // cp是一个地址,不为0。再取*cp的值,为一个字符串,不为0.最后两者相与,值> 为真。

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

void test410()
  {
      int number = 0;
      while (cin >> number) 
      {   
          if (42 == number)
          {   
              break;
          }   
      }   
  }

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

if ((a>b) && (b>c) && (c>d))

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

先取i值,再计算j < k是否成立,成立为大于0的数,否则为0。再计算i != (j < k) 。

4.13 下述语句中,赋值完i和d的值分别是多少?

     d = i = 3.5;          // i = 3, d = 3; i = 3.5, 将3.5隐式转化为整型。
      i = d = 3.5;         // d = 3.5, i = 3.4.14

4.14 执行下述if语句会发生什么情况。

if (42 = i)            //报错,不能对常量42赋值。
if (i = 42)            // 将42赋值给i,if条件为真。

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

    double dval;
      int ival;
      int *pi;

     dval = ival = pi = 0;            // pi存的是地址,给pi赋值为0,即要访问地址为0的内存?
// 应改为:
    dval = ival = *pi = 0;

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

 if (p = getPtr != 0){}       // 可能会先判断!=, 再把结果赋值给p,改为if ((p = getPtr) != 0)
 if (i = 1024)                    // 会把1024赋值给i,再进行判断,改为if (1024 == i)

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

会先向后移动一个元素,再输出移动后的值。输出范围为第二个元素到最后一个元素的下一个元素。由于最后一个元素的下一个元素未知,所以会访问到未知内存。

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

ptr != 0 && *ptr++;        //判断ptr是否为空,并且ptr指向的值是否为0;
ival ++ && ival;                // 判断ival及ival++是否为空。运算的顺序为,先计算++,在进行&&
vec[ival++] <= vec[ival];    // 运算顺序未知,比如ival = 1,编译器有可能先算vec[ival++],得到ival=2,再计算vec[ival],这样就得不到预期结果。因此改成vec[ival+1] <= vec[ival];

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

*iter++;                    // 合法,先对iter+1,再返回iter初始值的副本,再对该副本进行解引用
(*iter)++;                  //  不合法,不能对string类型进行++操作。
*iter.empty();           // 不合法,不能对指针指向的值判空。可改为(*iter).empty();
iter->empty();          // 合法
++*iter;                    // 不合法,先求*iter,在进行++操作,不能对string类型做++操作,可改为 *(++iter);
iter++->empty();      //合法,++和->的优先级一样,所以遵循自左向右结合,先算iter++的值,返回iter初始值的副本,再进行empty()判断。iter->empty等价于(*iter).empty

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

void test421()
  {
      vector<int> ivec = {1, 2, 3, 4, 5, 6}; 

      for (auto &iter : ivec) {
          iter = (iter % 2 == 1) ? iter * 2 : iter;
          cout << iter << " ";
      }   
      cout << endl;
}

4.22

void test422()
  {   
      int grade;
      string finalgrade;

      while (cin >> grade)
      {
      // approach 1
          finalgrade = (grade > 90) ? "high pass" : ((grade < 60) ? "fail" : ((grade < 75) ? "low pass" : "pass"));

      // approch 2    
          if (grade > 90)
          {   
              finalgrade = "hign pass";
          }
          else if (grade < 60)
          {   
              finalgrade = "fail";
          }
          else if (grade < 75)
          {   
              finalgrade = "low pass";
          }
          else
          {   
              finalgrade = "pass";
          }

          cout << finalgrade << endl;
      }
  }

4.23 指出下面表达式的问题,并说明如何修改。

    string s = "word";
//    string p1 = s + s[s.size()-1] == 's' ? "" : "s";
 //  条件语句优先级较低,要给其加括号。
   string p1 = s + (s[s.size()-1] == 's' ? "" : "s");

4.24 假如条件运算符满足的是左结合律,求值过程会是怎样的?

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

'q' << 6运算会把结果隐式转化为int类型,因为6是int类型。因此结果的二进制码为:
0000 0000 0000 0000 0001 1100 0100 0000,转化为10进制为7232.

4.26 测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况。
> 在某些系统上,int占16为,因此如果用unsigned int来存储的话,在这些机器上就会发生位数不够用的情况。而unsigned long则可以保证在任何机器上都至少有32位。

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

unsigned long ul1 = 3, ul2 = 7;
cout << (ul1 & ul2) << endl;            // 0011 & 0111 = 0011 = 3
cout << (ul1 | ul2) << endl;              // 0011 | 0111 = 0111 = 7
cout << (ul1 && ul2) << endl;          // 3 && 7 = 1
 cout << (ul1 || ul2) << endl;4.28     // 3 || 7 = 1

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

void test428()
  {   
      cout << sizeof (int) << endl;                // 4
      cout << sizeof (char) << endl;             // 1
      cout << sizeof (short) << endl;            // 2
      cout << sizeof (long) << endl;             // 8
      cout << sizeof (float) << endl;              // 4
      cout << sizeof (double) << endl;          // 8
      cout << sizeof (long double) << endl;  // 16
      cout << sizeof (long long) << endl;      // 8
  }

4.29 推断下面代码的输出,说明原因。

void test429()
  {
      int x[10];
      int *p = x;

      cout << sizeof(x)/sizeof(*x) << endl;         // 10, sizeof(x) = 10*4, sizeof(*x) = 4;
      cout << sizeof(p) / sizeof (*p) << endl;     // 2, sizeof(p)的含义:p是一个int *类型,因此得出的大小应该是指针的大小。
                                                                      // sizeof(*p)的含义:*p已经对p解引用了,*p实际就是int类型,因此sizeof(*p)得到的是一个int型的大小。
  }

4.30 在下面的表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同。

sizeof x +  y;                // sizeof(x) +  y; 
sizeof p->mem[i];        // sizeof (p->mem[i]); 
sizeof a < b;                // sizeof (a) < b;   
sizeof f();                     // sizeof (f()); 

4.31 使用递增和递减运算符时,为什么要用前置版本而不用后置版本,要想使用后置版本需要做哪些改动?

在for循环中使用前置版本和后置版本都能得到相同的结果。这里使用前置版本的原因,就是4.5节中的建议所述:“前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容,如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。”

for ( init; condition; increment )
{
   statement(s);
}

for循环的执行流程:
1、先执行init,且只执行一次。
2、判断condition,如果为真,则执行循环主体。
3、循环主体执行完之后再执行increment,更新循环控制变量。
改为后置版本如下:

void test431()
  {
      vector<int> ivec(10, 0); 

      vector<int>::size_type cnt = ivec.size();

      for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt++)
      {   
          cout << "ix = " << ix << " cnt = " << cnt << endl; 
          ivec[ix] = cnt;
      }   
  }

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)
      {   
          /* ... */
      }

循环遍历ia数组。ix和ptr的作用相同,一个使用下标遍历,一个使用指针遍历。

4.33 说明下面表达式的含义。

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

因为逗号表达式的优先级最低,按照预期,如果someValue为真,冒号后面的语句不会再执行了,但实际上,编译器会认为冒号后面的--x属于三目运算符中的语句,而--y属于一个单独的语句。也就是( someValue ? ++x, ++y : --x), --y; 因此,如果someValue为真,则执行++x,++y,--y,最后得到的结果是y本身。如果someValue为假,则执行--x,--y,最终的结果是--y的值。

4.34 说明下面的表达式中将会发生什么样的类型转化。

if (fval)                        // float类型转化为bool类型
dval = fval + ival;        // int先转化为float,然后赋值给dval时再转化为double类型
dval + ival * cval;        // char先转化为int,然后int转化为double

4.35 假设有如下定义,则下面表达式中是否发生了隐式转化?

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

cval = 'a' + 3;                             // ‘a’先转化为int进行计算,得到的结果再转化为char
fval = ui - ival * 1.0;                //  ival先转化为double,ui也转化为double,double再截为float
dval = ui * fval;                           // unsigned int先提升为float,float再转为double
cval = ival + fval + dval;             // ival先转为float,float再转为double,最后double转为char

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;

pv = (void *)ps;            // pv = const_cast<string *>(ps); 去除底层const属性。
i = int(*pc);                  // i = static_cast<int>(*pc);
pv = &d;                      // pv = static_cast<void *>(&d);
pc = (char *)pv;           // pc = reinterpret_cast<char *> (pv);

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

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

将j/i的结果转化为double类型,再赋值给slope。

posted @ 2018-05-31 14:20  安月月  阅读(2398)  评论(3编辑  收藏  举报