C++ Primer(第五版)|练习题答案与解析(第四章:表达式)

C++ Primer(第五版)|练习题答案与解析(第四章:表达式)

本博客主要记录C++ Primer(第五版)中的练习题答案与解析。
参考:C++ Primer
C++ Primer

练习题4.1

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

105,P122

练习题4.2

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

(a) *vec.begin()–> (*vec.begin())
(b) *vec.begin() + 1 --> (*vec.begin() + 1)

练习题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 = -86
(b)-30 + 3 * 21 / 5 = -18
(c)30 / 3 * 21 % 5 = 0
(d)-30 / 3 * 21 % 4 = -2

练习题4.6

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

   int num = 10;
   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的值,为一个字符串,是"Hello World",不为0.最后两者相与,值为True。

练习题4.10

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

代码:

#include <iostream>
using namespace std;
int main()
{
      int number = 0;
      while (cin >> number)
      {
          if (42 == number)
          {
              break;
          }
      }
}

测试:

1
2
42

练习题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的值分别是多少?

语句:int i; double d;
(a)d = i = 3.5; i = 3, d = 3; i = 3.5, 将3.5隐式转化为整型。
(b)i = d = 3.5; d = 3.5, i = 3,因为i是整型。

练习题4.14

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

(a)if (42 = i) 会报错,42是常量,不能赋值。
(b)if (i = 42)将42赋值给i,if条件为真。

练习题4.15

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

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

pi是地址,不能把指针的值赋值给int(P129)
修改为:dval = ival = *pi = 0;

练习题4.16

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

(a)if (p = getPtr() != 0){} 这是因为赋值运算优先级较低(P130),可能会先判断!=, 再把结果赋值给p,修改为:if ((p = getPtr) != 0)
(b)if (i = 1024) 会把1024赋值给i,再进行判断,改为if (i == 1024)

练习题4.17

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

前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回(P132)。

练习题4.18

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

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

练习题4.19

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

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

练习题4.20

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

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

练习题4.21

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

代码:

#include <iostream>
#include <vector>
#include <iterator>
#include <string>

using namespace std;
int main()
{
      vector<int> ivec = {1, 2, 3, 4, 5, 6};

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

输出:

2 2 6 4 10 6

练习题4.22

编写程序,将成绩从high pass, pass, fail扩充一种:60-75为low pass,使用两种方法:1.使用条件运算符。2.使用if-else。

代码:

#include <iostream>
#include <vector>
#include <iterator>
#include <string>

using namespace std;
int main()
{
      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";

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

练习题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;

(a)cout << (ul1 & ul2) << endl; 0011 & 0111 = 0011 = 3
(b)cout << (ul1 | ul2) << endl; 0011 | 0111 = 0111 = 7
(c)cout << (ul1 && ul2) << endl; 3 && 7 = 1
(d)cout << (ul1 || ul2) << endl; 3 || 7 = 1

练习题4.28

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

代码:

#include <iostream>
#include <vector>
#include <iterator>
#include <string>

using namespace std;
int main()
{
    // by using method below only include what is needed.
    using std::cout;
    using std::endl;

    // void type
    cout << "void: nullptr_t\t" << sizeof(std::nullptr_t) << " bytes" << endl << endl;

    // boolean type
    cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;

    // charactor type
    cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
    cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
    cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
    cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;

    // integers type
    cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
    cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
    cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
    cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;

    // floating point type
    cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
    cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
    cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;

    // Fixed width integers
    cout << "int8_t:\t\t" << sizeof(int8_t) << " bytes" << endl;
    cout << "uint8_t:\t" << sizeof(uint8_t) << " bytes" << endl;
    cout << "int16_t:\t" << sizeof(int16_t) << " bytes" << endl;
    cout << "uint16_t:\t" << sizeof(uint16_t) << " bytes" << endl;
    cout << "int32_t:\t" << sizeof(int32_t) << " bytes" << endl;
    cout << "uint32_t:\t" << sizeof(uint32_t) << " bytes" << endl;
    cout << "int64_t:\t" << sizeof(int64_t) << " bytes" << endl;
    cout << "uint64_t:\t" << sizeof(uint64_t) << " bytes" << endl;

    return 0;
}

输出:

oid: nullptr_t 4 bytes

bool:           1 bytes

char:           1 bytes
wchar_t:        2 bytes
char16_t:       2 bytes
char32_t:       4 bytes

short:          2 bytes
int:            4 bytes
long:           4 bytes
long long:      8 bytes

float:          4 bytes
double:         8 bytes
long double:    12 bytes

int8_t:         1 bytes
uint8_t:        1 bytes
int16_t:        2 bytes
uint16_t:       2 bytes
int32_t:        4 bytes
uint32_t:       4 bytes
int64_t:        8 bytes
uint64_t:       8 bytes

练习题4.29

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

代码:

#include <iostream>
#include <vector>
#include <iterator>
#include <string>

using namespace std;
int main()
{
      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;
      cout << sizeof(p)  << endl;
      cout << sizeof (*p) << endl;
      // sizeof(p)的含义:p是一个int *类型,因此得出的大小应该是指针的大小。
      // sizeof(*p)的含义:*p已经对p解引用了,*p实际就是int类型,因此sizeof(*p)得到的是一个int型的大小。
}

输出:

10
1
4
4

练习题4.30

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

(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.31

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

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

#include <iostream>
#include <vector>
#include <iterator>
#include <string>

using namespace std;
int main()
{
      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

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

(a)if (fval) float类型转化为bool类型
(b)dval = fval + ival; int先转化为float,然后赋值给dval时再转化为double类型
(c)dval + ival * cval; char先转化为int,然后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先转化为double,ui也转化为double,double再截为float
(c)dval = ui * fval; unsigned int先提升为float,float再转为double
(d)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;

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

练习题4.38

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

表达式:double slope = static_cast<double>(j/i);
含义:将j/i的结果转化为double类型,再赋值给slope。

发布了76 篇原创文章 · 获赞 44 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_24739717/article/details/104157789