C++Primer练习[第四章]

//练习4.1:表达式5 + 10 * 20 / 2的求值结果是多少?
//答:105。计算顺序为:(5 + ((10 * 20) / 2))


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


//练习4.3 C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说明你的理由。
//答;可以接受,只要求值顺序对最终结果不造成影响,都可以接受,因为在同一表达式中,同时出现了许多二元运算符,编译器可以选择一个一个去求解,也可以分开单独来运算,但只要最后的结果正确就可以。


//练习4.4 在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出其结果验证之前的推断。
//12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
//答:(((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2))
//先求(12 / 3) = 4
//再求(4 * 4) = 16
//再求(5 * 15) = 75
//再求(24 % 4) = 0
//再求(0/2) = 0
//再求16 + 75 + 0 = 91
#include <iostream>
using std::cout;
using std::endl;
int main()
{
auto res = 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2;
cout << res << endl;


system("pause");
return 0;
}


//练习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 写出一条表达式用于确定一个整数是奇数还是偶数。
//答:a%2  结果为0是偶数,结果为1是奇数


//练习4.7 溢出是何含义 ? 写出三条将导致溢出的表达式。
//答:当计算的结果超出该类型所能表示的范围时就会产生溢出。
//1.short short_value = 32767 + 1;
//2.short short_value = 32767 * 2;
//3.short short_value = -32768 - 1;


//练习4.8 说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。
//答:逻辑与运算符中,先求左边运算对象的值,当且仅当结果为真时,计算右边运算对象的值
//逻辑或运算符中,先求左边运算对象的值,当且仅当结果为假时,计算右边运算对象的值
//相等性运算符中,求值顺序不确定。


//练习4.9 解释在下面的if语句中条件部分的判断过程。
//const char *cp = "Hello World";
//if (cp && *cp)
//答:先判断cp是否为真,结果(cp不为空指针)为真,接着判断*cp是否为真,结果(*cp的值为'H')为真,则判断出if语句的条件为真。


//练习4.10 为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止。
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
cout << "为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止" << endl;
int i = 0;
cin >> i;
while (i != 42)
{
cin >> i;
}
cout << "遇到42停止" << endl;
system("pause");
return 0;
}


//练习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是否为真,再计算 != 运算符,如果j<k为真,则i!=1时为真。如果j<k为假,则i!=0为真。


//练习4.13 在下列语句中,当赋值完成后i和d的值分别是多少?
//int i; double d;
//(a)d = i = 3.5; (b)i = d = 3.5;
//答:(a) i的值为3, d的值为3.0
//(b)i的值为3, d的值为3.5


//练习4.14 执行下述if语句后将发生什么情况?
//if (42 = i) // ...
//if (i = 42) // ...
//答:if (42 = i)会报错,42是一个常量,不是左值。
//if (i = 42)会执行给i赋值42,if语句括号里表达式的值为42,为真。


//练习4.15 下面的赋值是非法的,为什么?应该如何修改?
//double dval; int ival; int *pi;
//dval = ival = pi = 0;
//答:因为pi是一个指针,类型与ival不同,且pi的类型(int *)无法转换成ival的类型(int),所以是非法的。
//修改为:
//double dval; int ival; int *pi;
//pi = ival = dval = 0;


//练习4.16 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
//(a) if (p = getPtr() != 0)
//(b) if (i = 1024)
//答:(a)因为赋值运算符的优先级比!=运算符低,会先进行getPtr()!=0的判断,再将得到的布尔值赋值给p。
//可修改为 if ((p = getPtr()) != 0)
//(b)括号里是一个赋值语句,返回值是1024,不为0,为真。
//可修改为 if (i == 1024)


//练习4.17 说明前置递增运算符和后置递增运算符的区别。
//答:前置递增运算符是先将运算对象加1,然后将改变后的对象作为求值结果。
//后置递增运算符也会将运算对象加1,但是求值结果是运算对象改变之前的那个值的副本。


//练习4.18 如果第132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?
//auto pbeg = v.begin();
//while(pbeg != v.end() && *pbeg >= 0)
// cout <<  *pbeg++ << endl;
//答:如果使用前置递增运算符,则*++pbeg是对pbeg加1以后的值进行解引用。解引用该值将产生错误的结果。不但无法输出第一个元素,而且如果序列中没有负值,程序将可能试图解引用一个根本不存在的元素。


//练习4.19 假设ptr的类型是指向int的指针、vec的类型是vector<int>、ival的类型是int, 说明下面的表达式是何含义?如果表达式不正确,为什么?应该如何修改?
//(a)ptr != 0 && *ptr++ (b)ival++ && ival
//(c)vec[ival++] <= vec[ival]
//答:(a)含义是先判断ptr是否不是空指针,如果不是空指针,则将ptr加1,然后解引用ptr加1之前的值的副本,判断结果是否为真。
//(b)含义是先对ival加1,判断ival加1之前的值是否不为0,如果为真,则判断当前ival的值(ival加1后)是否不为0
//(c)表达式不正确。因为<=运算符的运算对象的求值顺序不确定。在一条子表达式改变了某个运算对象的值,另一条子表达式又要使用该值的话,就会有问题。
//该语句是未定义的。


//练习4.20:假设iter的类型是vector<string>::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
//(a)*iter++; (b)(*iter)++; (c)*iter.empty()
//(d)iter->empty() (e)++*iter; (f)iter++->empty();
//答:(a)合法。含义是对iter加1,解引用iter加1之前的值的副本。
//(b)不合法。对iter解引用得到一个string对象,string对象没有++的操作。
//(c)不合法。iter是vector<string>::iterator,没有empty()的成员。
//(d)合法。调用iter这个迭代器所指对象(string)的成员empty()。等价于(*iter).empty()
//(e)不合法。前置++与*的优先级相同,且都是右结合律。所以++*iter;相当与++(*iter)。但是iter解引用得到一个string对象,string对象没有++的操作。
//(f)合法。对iter加1,然后对加1之前的iter的副本调用empty()成员。虽然->的优先级比后置++高。但是因为iter在此情况下只能与++结合,所以相当于(iter++)->empty();


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


//练习4.22 本节的示例程序将成绩划分成high pass, pass 和fail三种,扩展改程序使其进一步将60分到75分之间的成绩设定为low pass。
//要求程序包含两个版本:一个版本只使用条件运算符;另外一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?
//答:使用1个或多个if语句会更容易理解,逻辑更清晰,可读性更好。
//版本1
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
int main()
{
cout << "只使用条件运算符:" << endl;
int grade = 70;
string finalgrade = (grade > 90) ? "high pass" 
: (grade < 60) ? "fail" 
: (grade <= 75) ? "low pass" : "pass";
cout << finalgrade << endl;
system("pause");
return 0;

}



//版本2
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
int main()
{
cout << "使用1个或多个if语句:" << endl;
int grade = 70;
string finalgrade;
if (grade > 90)
{
finalgrade = "high pass";
}
else if (grade < 60)
{
finalgrade = "fail";
}
else if (grade <= 75)
{
finalgrade = "low pass";
}
else
{
finalgrade = "pass";
}
cout << finalgrade << endl;
system("pause");
return 0;
}


//练习4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
//string s = "word";
//string p1 = s + s[s.size() - 1] == 's' ? "" : "s";
//答:优先级 [] 等于 () 大于 + 大于 == 大于 ?: 大于 =
//等同于 string (p1 = (((s + (s[s.size() - 1])) == 's') ? "" : "s"));
//s[s.size() - 1]得到字符'd',s+'d'得到字符串"wordd",字符串不能与字符进行对比。
//应修改为:
//string s = "word";
//string p1 = s + (s[s.size() - 1] == 's') ? "" : "s";


//练习4.24 本节示例程序将成绩划分成high pass, pass 和fail三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值过程将是怎样的?
// string finalgrade = (grade > 90) ? "high pass" 
// : (grade < 60) ? "fail" : "pass";
//答:原本满足右结合律相当于如下:
// string finalgrade = ((grade > 90) ? "high pass" : ((grade < 60) ? "fail" : "pass"));
//如果是左结合律 则相当于:
// string finalgrade = (((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass");
//结果就是先算(grade > 90)是否为true,不是的话条件运算表达式返回(grade < 60)的结果,在根据这个表达式的结果返回"fail" 或者"pass"


//练习4.25 如果一台机器上int占32位、char占8位,用的是Latin-1字符集,其中字符'q'的二进制形式是01110001,那么表达式~'q'<<6的值是什么?
//答:位求反比向左位移优先级高。表达式相当于(~'q')<<6。
//因为如果运算对象是小整型,他的值会被自动提升成较大的整数类型
//~'q'相当于 ~00000000 00000000 00000000 01110001 计算结果为 11111111 11111111 11111111 10001110
//然后计算10001110<<6 ,所以结果为
//11111111 11111111 11100011 10000000


//练习4.26 在本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况?
//答:int类型只能确保占用16位,而例子中至少需要27位,如果在int占用16位的机子上将有可能会丢失数据。


//练习4.27 下列表达式的结果是什么?
//unsigned long ul1 = 3, ul2 = 7; 
//(a)ull & ul2 (b) ul1 | ul2
//(c)ul1 && ul2 (d) ul1 || ul2
//答:(a)相当于 
// 00000000 00000000 00000000 00000011 
//   & 00000000 00000000 00000000 00000111
//结果为 00000000 00000000 00000000 00000011 即为3
//(b)相当于
// 00000000 00000000 00000000 00000011 
//   | 00000000 00000000 00000000 00000111
//结果为 00000000 00000000 00000000 00000111 即为7
//(c)因为ul1不为0(即结果为true)且ul2不为0(即结果为true) 所以表达式的结果是true
//(d)因为ul1不为0(即结果为true) 所以表达式结果为true


//练习4.28 编写一段程序,输出每一种内置类型所占空间的大小。
#include <iostream>
using std::cout;
using std::endl;
int main()
{
cout << "sizeof(bool):" << sizeof(bool) << endl;

cout << "sizeof(char):" << sizeof(char) << endl;
cout << "sizeof(signed char):" << sizeof(signed char) << endl;
cout << "sizeof(unsigned char):" << sizeof(unsigned char) << endl;

cout << "sizeof(wchar_t):" << sizeof(wchar_t) << endl;
cout << "sizeof(char16_t):" << sizeof(char16_t) << endl;
cout << "sizeof(char32_t):" << sizeof(char32_t) << endl;

cout << "sizeof(short):" << sizeof(short) << endl;
cout << "sizeof(int):" << sizeof(int) << endl;
cout << "sizeof(long):" << sizeof(long) << endl;
cout << "sizeof(long long):" << sizeof(long long) << endl;

cout << "sizeof(unsigned short):" << sizeof(unsigned short) << endl;
cout << "sizeof(unsigned int):" << sizeof(unsigned int) << endl;
cout << "sizeof(unsigned long):" << sizeof(unsigned long) << endl;
cout << "sizeof(unsigned long long):" << sizeof(unsigned long long) << endl;

cout << "sizeof(float):" << sizeof(float) << endl;
cout << "sizeof(double):" << sizeof(double) << endl;
cout << "sizeof(long double):" << sizeof(long double) << endl;
system("pause");
return 0;
}


//练习4.29 推断下面代码的输出结果并说明理由。实际运行这段程序。结果和你想象的一样吗?如果不一样,为什么?
//int x[10]; int *p = x;
//cout << sizeof(x) / sizeof(*x) << endl; 
//cout << sizeof(p) / sizeof(*p) << endl;
//答:10 因为数组总大小/每个元素的大小 = 元素个数
//32位下为1,64位下为2。 因为指针的大小在32位机上是4字节,在64位上是8字节,*p即x[0]也就是一个int。 指针大小/int大小 = 1(32位)或者2(64位)
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int x[10]; int *p = x;
cout << sizeof(x) / sizeof(*x) << endl; 
cout << sizeof(p) / sizeof(*p) << endl;
system("pause");
return 0;
}


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


//练习4.31 本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。
//要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
//答:因为前置版本的递增运算符和递减运算符避免了不必要的工作,它把值加1或者减1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。
//将++ix,--cnt改为ix++,cnt--
//vector<int>::size_type cnt = ivec.size();
//for(vector<int>::size_type ix = 0;ix != ivec.size();ix++,cnt--)
//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) { /* . . . */}
//答:ptr初始化指向数组ia的首地址,ix初始化为0,判断ix是否不超过数组下标且ptr指向的数组的对象,是的话执行循环体,之后对ix 和 ptr都进行递增。


//练习4.33 根据4.12节中的表(第147页)说明下面这条表达式的含义。
//someValue ? ++x, ++y : --x, --y
//答:?:的优先级比,高,所以表达式相当于 (someValue ? ++x, ++y : --x), --y 
//++x,++y是?:中的一个运算对象。
//如果someValue值为真,则计算(++x, ++y)的值,对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果丢弃掉。逗号运算符真正的结果是右侧表达式的值。所以值就是++y的值。
//如果someValue值为假,则计算--x的值。
//然后执行--y, 整个表达式最后的结果是--y的值。


//练习4.34 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换:
//(a)if (fval) (b)dval = fval + ival; (c)dval + ival * cval; 
//需要注意每种运算符遵循的是左结合律还是右结合律
//答:(a)fval转换成bool
//(b)ival转换成float,fval + ival的结果转换成double。 +是左结合律,=是右结合律
//(c)cval转换成int,ival * cval的结果转换成double。+*都是左结合律。


//练习4.35 假设有如下的定义:
//char cval; int ival; unsigned int ui;
//float fval; double dval;
//请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。
//(a)cval = 'a' + 3; (b)fval = ui – ival * 1.0;
//(c)dval = ui * fval; (d)cval = ival + fval + dval;
//答:(a)'a'转换成int。表达式结果转换成char。
//(b)ival转换成float。如果float类型占用的空间比int多,则ui转换成float,否则ival * 1.0的结果转换成unsigned int。表达式结果转换成float。
//(c)如果float类型占用的空间比int多,则ui转换成float,否则fval转换成unsigned int。表达式结果转换成double。
//(d)ival转换成float。ival + fval的结果转换成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; (b)i = int(*pc);
//(c)pv = &d; (d)pc = (char*)pv;
//答:(a)pv = static_cast<void*>(ps);
//(b)i = static_cast<int>(*pc);
//(c)pv = static_cast<void*>(&d);
//(d)pc = static_cast<char*>(pv);


//练习4.38 说明下面表达式的含义
//double slope = static_cast<double>(j / i);
//答:将j/i的结果强制转换为double类型,把转换后的结果赋值给slope

猜你喜欢

转载自blog.csdn.net/sukeychen/article/details/80373518