C++ Primer 习题(第四章附答案)

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

#include
using namespace std;

int main()
{
	cout << 5 + 10 * 20 / 2 << endl;
	return 0;
}

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

#include
#include
#include
#include

using namespace std;

int main()
{
	vector<int>vec;
	srand((unsigned)time(NULL));
	//系统自动为向量生成一组元素
	for (int i = 0; i != 10; i++)
	{
		vec.push_back(rand() % 100);
	}

	cout << "*vec.begin()的值是:" << *vec.begin() << endl;
	cout << "*(vec.begin())的值是:" << *(vec.begin()) << endl;
	cout << "*vec.begin()+1的值是:" << *vec.begin()+1 << endl;
	cout << "(*vec.begin())+1的值是:" << (*vec.begin())+1 << endl;

	return 0;
}

练习4.3 略

练习4.4 在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出其结果验证之前的推断。

#include
using namespace std;

int main()
{
	cout << 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2 << endl;
	return 0;
}

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

#include
using namespace std;

int main()
{
	cout << -30 * 3 + 21 / 5 << endl;
	cout << -30 + 3 * 21 / 5 << endl;
	cout << 30 / 3 * 21 % 5 << endl;
	cout << -30 / 3 * 21 % 4 << endl;
	return 0;
}

练习4.6 写出一条表达式用于确定一个整数是奇数还是偶数

#include
using namespace std;

int main()
{
	int num;
	cin >> num;
	if (num % 2 == 0)
	{
		cout << "even" << endl;
	}
	else
	{
		cout << "odd" << endl;
	}
	return 0;
}

练习4.7 溢出是何含义?写出三条将导致溢出的表达式。

//溢出是一种常见的算术运算错误。因为在计算机中存储某种类型的内存空间有限,
//所以该类型的表达能力也是有限的,当计算的结果值超出这个范围时,就会产生未定义的数值,这种错误称为溢出
//假定编译器规定int占32位,则下面的3条表达式都将产生溢出错误
int i = 2147483647 + 1;
int j = -100000 * 300000;
int k = 2015 * 2015 * 2015 * 2015;

练习4.8 说明在逻辑与逻辑或及相等性运算符中运算对象求值顺序。

  逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,
当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种
策略就是短路求值。其策略是:对于逻辑与运算符来说,当且仅当左侧运算对象为
真时才计算右侧运算对象;对于逻辑或运算符来说,当且仅当左侧运算对象为假时
才计算右侧运算对象。

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

const char* cp = "hello world";
if (cp && *cp)
cp指向字符串的指针,因此上式的条件部分含义是首先检查指针cp是否有效。如果cp为空指针
或无效指针,则条件不满足。如果cp有效,即cp指向了内存中的某个有效地址,继续解引用指针
cp并检查cp所指的对象是否为空字符'\0',如果cp所指的对象不是空字符则满足条件;否则不满足
上式显然初始状态下cp指向了字符串的首字符,是有效的;同时当前cp所指的对象是字符h,不是
空字符,所以if的条件部分为真。

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

#include
using namespace std;

int main()
{
	int num;
	while (cin>>num&&num!=42)
	{
		cout << num << endl;
	}
	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

c++规定< <= > >= 的优先级高于==和!=,因此上式的求值过程等同于i!=(j<k),意即先比较j和k的大小,得到的结果是一个布尔值(10);然后判断i的值与之是否相等

练习4.13 在下述语句中,当赋值完成后的i和d的值分别是多少?

#include
using namespace std;

int main()
{
	int i;
	double d;
	d = i = 3.5;
	cout << d << " " << i << endl;  //i = 3;d =3;

	i = d = 3.5;
	cout << d << " " << i << endl;  //i = 3;d= 3.5;
	return 0;
}

练习4.14 执行下述if语句后将发生什么情况

if42=i) //赋值运算符的左侧运算对象必须是左值,字面值常量42显然不是左值,不能作为左侧运算对象。

if(i=42//42赋值给i,因为非0整数转换成布尔值都对应true,所以该条件恒为真
           //如果要判断i的值是否为42,应写成i==42

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

#include
using namespace std;

int main()
{
	double dval;
	int ival;
	int* pi;

	dval = ival = pi = 0;   //pi = 0 ,表示pi是一个空指针,接下来ival=pi试图把整型指针的值赋给整数,不符合语法规范
	//dval = ival = 0;
	//pi = 0;
	return 0;
}

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

(a)if((p = getPtr())!=0)

(b)if(i ==1024)

练习4.17 说明前置递增运算符和后置递增运算符的区别

前置版本首先将运算符对象加1(或减1),然后把改变后的对象作为求值结果
后置版本也将运算对象加1(或减1),但是求值结果是运算对象改变之前那个值的副本
两种运算符必须作用于左值运算对象。前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。

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

一:无法输出vector对象的第一个元素;
二:当所有元素都不为负时,移动到最后一个元素的地方,程序试图继续向前移动迭代器并解引用一个根本不存在的元素。

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

(a)ptr!=0&&*ptr++
//a的含义是先判断指针ptr是否为空,如果不为空,继续判断指针ptr所指的整数是否为非0数。
//如果非0,则表达式的最终求值结果为真;否则为假。最后把指针ptr向后移动一位
(b)ival++&&ival
//b的含义是先检查ival的值是否非0,如果非0继续检查(ival+1)的值是否非0.只有当两个值都是非0值时,表达式的求值结果为真;否则为假
//如果二元运算符的两个对象涉及同一个对象并改变对象的值,则这是一种不好的程序写法
//ival && (ival+1)
(c)vec[ival++]<=vec[ival]
//c的含义是比较vec[ival]和vec[ival+1]的大小,如果前者较小则求值结果为真,否则为假
//与b一样,应该为vec[ival]<=vec[ival+1]

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

(a)*iter++
//合法,后置递增运算符的优先级高于解引用运算符,其含义是解引用当前迭代器所处位置的对象内容,然后把迭代器的位置向后移动一位。
(b)(*iter)++
//非法,解引用iter得到vector对象当前的元素,结果是一个string,显然string没有后置递增操作
(c)*iter.empty()
//非法,解引用运算符的优先级低于点运算符,所以该式先计算iter.empty(),而迭代器并没有定义empty函数,所以无法通过编译。
(d)iter->empty()
//合法,iter->empty()等价于(*iter).empty().解引用迭代器得到迭代器当前所指的元素,结果是一个string,显然字符串可以判断是否为空,empty函数在此处有效
(e)++*iter
//非法,该式先解引用,得到迭代器当前所指的元素,结果是一个string,显然string没有后置递增操作
(f)iter++->empty()
//合法,iter++->empty()相当于(*iter++).empty().含义是解引用迭代器当前位置的对象内容,得到一个字符串,判断该字符串是否为空,然后把迭代器向后移动一位

练习4.21 编写一段程序,使用条件运算符从vector< int >中找到那些元素是奇数,然后将这些奇数值翻倍。

#include
#include
#include
#include

using namespace std;

int main()
{
	vector<int>vint;
	const int sz = 10;
	srand((unsigned)time(NULL));

	//使用普通for循环为数组赋初值
	for (int i = 0; i !=sz; i++)
	{
		vint.push_back(rand() % 100);
		cout << vint[i] << " ";
	}
	cout << endl;

	//使用范围for循环把数组中的奇数翻倍
	for (auto& c : vint)
	{
		if (c % 2 == 1)
		{
			c = c * 2;
		}
	}

	//使用范围for循环和迭代器输出数组的当前值
	for (auto it = vint.cbegin(); it != vint.cend(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
	return 0;
}

练习4.22 本节的示例程序将程序划分为high,pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设定为low pass。要求程序包含有两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解些呢?为什么?

#include
#include
using namespace std;

int main()
{
	string finalgrade;
	int grade;
	cout << "请输入您要检查的成绩: " << endl;
	//确保输入的成绩合法
	while (cin >> grade && grade >= 0 && grade <= 100)
	{
		//使用三层嵌套的条件表达式
		finalgrade = (grade > 90) ? "high pass"
			: (grade > 75) ? "pass"
			: (grade > 60) ? "low pass" : "fail";
		cout << "该成绩所处的档次是:" << finalgrade << endl;
		cout << "请输入您要查的成绩: " << endl;
	}
	return 0;
}
#include
#include
using namespace std;

int main()
{
	string finalgrade;
	int grade;
	cout << "请输入您要检查的成绩:" << endl;
	//确保输入的成绩合法
	while (cin >> grade && grade >= 0 && grade <= 100)
	{
		//if语句实现
		if (grade > 90)
		{
			finalgrade = "high pass";
		}
		else if (grade > 75)
		{
			finalgrade = "pass";
		}
		else if (grade > 60)
		{
			finalgrade = "low pass";
		}
		else
		{
			finalgrade = "fail";
		}
		cout << "该成绩所处的档次是:" << finalgrade << endl;
		cout << "请输入您要检查的成绩:" << endl;

	}
	return 0;
}

练习4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如果修改?

   题目中的几个运算符的优先级次序从高到低是加法运算符,相等运算符,条件运算符和赋值运
 算符,因此式子的求值过程是先把s和s[s.size()-1]相加得到一个新字符串,然后把字符串
 与字符‘s’比较是否相等,这是一个非法操作,并且与程序愿意不符。
   要想实现程序的愿意,即先判断字符串s的最后一个字符是否是‘s’,如果是,什么也不做;如
 果不是,在s的末尾添加一个字符‘s’,我们应该添加括号强制限定运算符的执行顺序。

   
string pl = s + (s[s.size()-1] =='s'?"":"s")

练习4.24 本节的示例程序将程序划分为high pass,pass和fail三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值过程将是怎样的?

finalgrade = (grade > 90)?"high pass"
                         :(grade < 60)?"fail":"pass";
//左结合律
finalgrade = ((grade>90)?"high pass":(grade<60))?"fail":"pase";
先考察grade>90是否成立,如果成立,第一个条件表达式的值为“high pass”;如果不成立,第
一个条件表达式的值为grade<60.这条语句无法编译通过的,因为条件运算符要求两个结果表达
式的类型相同或者可以互相转化。即使假设语句上通过,也就说,第一个条件表达式的求值结果
分为3种,分别是“high pass”,10.接下来根据第一个条件表达式的值求解第二个条件表达式
,求值结果是“fail”和“pass”,与期望不符。

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

在位运算中,运算符~的优先级高于<<,因此先对q按位求反,因为位运算符的运算对象应该是
整数类型,所以字符‘q’首先转换为整数类型。如题所示,char8位而int32位,所以字符
‘q’转换后得到00000000 00000000 00000000 01110001,按位取反得11111111 11111111
11111111 10001110;接着执行移位操作,得到11111111 11111111 11100011 10000000.
c++规定整数按照其补码形式存储,对上式求补,得到10000000 00000000 00011100 10000000,
即得到最终的二进制形式,转换成十进制形式是-7296.

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

c++规定unsigned long在内存中中至少占32位,这样就足够存放30个学生信息
c++规定unsigned int所占空间的最小值是16,该数据类型不足以存放全部学生的信息,从而造成了信息丢失,无法完成题目要求的任务。

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

#include
using namespace std;

int main()
{
	unsigned long ull = 3, u12 = 7;

	//按位与,结果为3
	cout << (ull & u12) << endl;

	//按位或,结果为7
	cout << (ull | u12) << endl;

	//逻辑与,结果为true
	cout << (ull && u12) << endl;

	//逻辑或,结果为true
	cout << (ull || u12) << endl;
	return 0;
}

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

#include
using namespace std;

int main()
{
	cout << "类型名称\t" << "所占空间" << endl;
	cout << "bool\t\t" << sizeof(bool) << endl;
	cout << "char\t\t" << sizeof(char) << endl;
	cout << "wchar_t\t\t" << sizeof(wchar_t) << endl;
	cout << "char16_t\t" << sizeof(char16_t) << endl;
	cout << "char32_t\t" << sizeof(char32_t) << endl;
	cout << "short\t\t" << sizeof(short) << endl;
	cout << "int\t\t" << sizeof(int) << endl;
	cout << "long\t\t" << sizeof(long) << endl;
	cout << "long long\t" << sizeof(long long) << endl;
	cout << "float\t\t" << sizeof(float) << endl;
	cout << "double\t\t" << sizeof(double) << endl;
	cout << "long double\t" << sizeof(long double) << endl;

	return 0;
}

C++ Primer 习题(第四章附答案)_第1张图片
练习4.29 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?

#include
using namespace std;

int main()
{
	//x为int数组包含10个元素
	int x[10];
	//p为一个指针,所指对象为变量x
	int* p = x;

	//sizeof(x)为10个int值所占的内存空间总和
	//sizeof(*x)相当于sizeof(int),即int所占的空间
	cout << sizeof(x) / sizeof(*x) << endl;

	//sizeof(p)的运算对象p是一个指针,求值结果是指针所占的空间大小
	//sizeof(*p)的运算对象*p是指针p所指的对象,即int变量x,所以求值结果是int值所占的空间大小
	cout << sizeof(p) / sizeof(*p) << endl;
	return 0;
}

练习4.30 根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号后的表达式的含义与原来的含义相同。

(a)sizeof x+y
sizeof运算符的优先级高于加法运算法的优先级,应该为sizeof(x+y)
(b)sizeof p->mem[i]
成员选择运算符的优先级高于sizeof的优先级,无需添加括号
(c)sizeof a<b
sizeof运算符的优先级高于关系运算符的优先级,应该为sizeof(a<b)
(d)sizeof f()
函数调用运算符的优先级高于sizeof的优先级,无需添加括号

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

#include
#include

using namespace std;

int main()
{
	vector<int>::size_type cnt = ivec.size();
	//将从size到1的值赋给ivec的元素
	for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--)
	{
		ivec[ix] = cnt;
	}
	return 0;
}

4.32 解释下面这个循环的含义

#include

using namespace std;

int main()
{
	//定义常量表达式size,值为5
	constexpr int size = 5;
	//以size作为维度创建一个整型数组ia,5个元素分别是1~5
	int ia[size] = { 1,2,3,4,5 };
	//定义整型指针指向数组ia的首元素,并且定义了一个整数ix,赋初值0;
	//当ix没有达到size同时指针ptr没有指向数组最后一个元素的下一个位置时,执行循环
	//变量ix和指针ptr分别执行递增操作
	for (int* ptr = ia, ix = 0; ix != size && ptr != ia + size; ++ix, ++ptr)
	{
		/*.......*/
	}
	return 0;
}

4.33 根据4.12节中的表说明下面这条表达式的含义

#include

using namespace std;

int main()
{
	int x = 10, y = 20;
	//检验条件为真的情况
	bool somevalue = true;
	//条件运算符的优先级高于逗号运算符
	somevalue ? ++x, ++y : --x, --y;
	cout << x << endl;
	cout << y << endl;
	cout << somevalue << endl;

	x = 10, y = 20;
	//检验条件为假的情况
	somevalue = false;
	somevalue ? ++x, ++y : --x, --y;
	cout << x << endl;
	cout << y << endl;
	cout << somevalue << endl;

	return 0;
}

练习4.34 根据本节给出的变量定义,说明下面的表达式将会发生什么样的类型转换

//float型变量fval自动转换成布尔值,转换规则是非0值转换为true,0转换为false
(a)if(fval)
//ival转换成float,与fval求和后所得结果进一步转换成double类型
(b)dval = fval +ival
//cval执行整型提升转换为int,与ival相乘后所得结果转换为double类型,最后在与dval相加
(c)dval +ival*cval

4.35 假设有如下定义 char cval; int ival; unsigned int ui;float fval;double dval

(a)cval = 'a'+3;
//字符a提升为int,与3相加所得的结果在转换为char并赋给cval。
(b)fval = ui - ival*1.0;
//ival转换为double,与1.0相乘的结果也是double类型,ui转换为double,相减,结果转换为float并赋给fval
(c)dval = ui *fval;
//ui转换float,与fval相乘的结果转换为double类型并赋给dval
(d)cval = ival+fval+dval;
//ival转换为float,与fval相加所得的结果转换为double类型,再与dval相加后结果转换为char类型

4.36 假设i是int类型,d是double类型,书写表达式i=d使其执行整数类型的乘法而非浮点型的乘法*

i* = static_cast<int>(d);
//使用static_cast把double类型的变量d强制转换成int类型

4.37 用命名的强制类型转换改写下列旧式的转换语句

int i;
double d;
const string *ps;
char *pc;
viod *pv;
(a)pv = (void*)ps;
(b)i = int(*pc);
(c)pv = &d;
(d)pc = (char*)pv;
//利用static_cast强制转换,对于底层const则使用const_cast
(a)pv = static_cast<void*>(const_cast<string*>(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

你可能感兴趣的:(C++,c++)