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
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
*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。