c++ Primer 第四章:表达式 练习答案记录
练习题导航
下面练习程序别忘记开头都加上这一语句
#include<iostream>
4.1节 基础
4.1.1 基本概念
4.1.2 优先级与结合律
练习4.1 表达式5+10*20/2的求值结果是多少
using namespace std;
int main()
{
int a = 5 + 10 * 20 / 2;
cout << a << endl;
}
练习4.2 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致
using namespace std;
int main()
{
string vec;
(*(vec.begin())); //(a)加括号
(*(vec.begin()) + 1); //(b)加括号
}
4.1.3 求值顺序
练习4.3 c++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的利用
可以,因为这样会使得c++运行时候代码的效率更快,而编译者应该尽量避免产生程序的潜在缺陷
4.2节 算术运算符
练习4.4 在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号)的表达式并输出其结果验证之前的推断
using namespace std;
int main()
{
int a = 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2;
int b = ((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2);
cout << a << " " << b << endl;
}
练习4.5 写成下列表达式的求值结果
using namespace std;
int main()
{
int a = -30 * 3 + 21 / 5; //-90 + 4 = -86
int b = -30 + 3 * 21 / 5; //-30 + 63/5 = -30 + 12 = -18
int c = 30 / 3 * 21 % 5; //10*21%5 = 210%5 = 0
int d = -30 / 3 * 21 % 4; //-10*21%4 = -210%4 = -2
cout << a << " " << b << " " << c << " " << d;
}
练习4.6 写成一条表达式用于确定一个整数是奇数还是偶数
using namespace std;
int main()
{
int a;
cout << "请输入一个整数,我将判断是奇数还是偶数" << endl;
while (cin >> a) {
if (a % 2 == 0) {
cout << "偶数" << endl;
}
else {
cout << "奇数" << endl;
}
}
}
练习4.7 溢出是何含义?写出三条将导致溢出的表达式
using namespace std;
int main()
{
int a = 2147483647; //2的31次方-1
a++;
cout << a << endl;
short b = 32767; //2的15次方-1
b++;
cout << b << endl;
}
4.3 逻辑和关系运算符
练习4.8 说明在逻辑与、逻辑或及相关性运算符中运算对象求值的顺序
逻辑与,先判断左边表达式是否为真,否输出false;若为真则判断右边表达式是否为真,真输出true,否输出false
逻辑或,先判断左边表达式是否为真,若为否则判断右边表达式是否为真,真输出true,否输出false;若为真直接输出true
相关性运算符先返回左边比较的结果,再将这个结果与右边表达式比较
练习4.9 解释在下面的if语句中条件部分的判断过程
using namespace std;
int main()
{
const char* cp = "Hello World";
if (cp && *cp); //先判断cp指针所指向的地址是否是空地址,是则输出false,不是则判断指针内的内容是否为0,是则输出false,否则输出true
}
练习4.10 为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止
using namespace std;
int main()
{
int i = 0;
while (cin >> i && i != 42);
}
练习4.11 书写一条表达式用于测试四个值a、b、c、d的关系,确保a大于b,b大于c,c大于d
using namespace std;
int main()
{
int a, b, c, d;
if (a > b && b > c && c > d);
}
练习4.12 假设i、j、k是三个整数,说明表达式i!=j<k的含义
首先判断i是否等于j,若等于,则判断k是否大于0,若不等于,则判断k是否大于1
4.4 赋值运算符
练习4.13 在下述语句中,当赋值完成后i和d的值分别是多少?
using namespace std;
int main()
{
int i;
double d;
d = i = 3.5; //i赋值5后赋值到d上,首先i值为3赋予d后,d=3
i = d = 3.5; //d值为3.5,赋值到i上后,i=3
}
练习4.14 执行下述if语句后将发生什么情况?
int main()
{
if (42 = i); //非法,42不能被赋值
if (i = 42); //i=42,i不等于0,true,则可以执行if语句
}
练习4.15 下面的赋值是非法的,为什么?应该如何修改?
int main()
{
double dval;
int ival;
int* pi;
dval = ival = pi = 0; //错误“=”: 无法从“int *”转换为“int” 应该改为*pi
}
练习4.16 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如果修改?
int main()
{
if (p = getptr() != 0); //应该在p后面再加一个=号
if (i = 1024); //在i后面加一个=号
}
4.5 递增和递减运算符
练习4.17 说明前置运算符和后置运算符的区别
前置运算符是对变量本身进行加,后置运算符将对象原始值的副本作为右值返回
练习4.18 如果第132页那个输出vector对象元素的while循环使用前置递增运算符将得到什么结果?
第一次循环则解引用第二个地址;最后一次循环,则解引用vector最后一个地址的后一个位置
练习4.19 假设ptr的类型是指向int的指针,vec的类型是vector、ival的类型是int,说明下面表达式是何含义?
如果表达式不正确,为什么?应该如何修改?
#include<vector>
using namespace std;
int main()
{
int* ptr;
vector<int> vec;
int ival;
ptr != 0 && *ptr++; //判断ptr是否为0
ival++&& ival; //首先判断ival是不是为0,不是的话判断ival+1是不是为0,不是输出true,是则输出false
vec[ival++] <= vec[ival]; //等价玉vec[ival]<=vec[ival+1]
}
4.6 成员访问运算符
练习4.20 假设iter的类型是vector::iterator,说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法错在何处?
#include<vector>
#include<string>
using namespace std;
int main()
{
vector<string>::iterator iter;
*iter++; //表示对iter解引用,并将iter指向下一个位置
(*iter)++; //错误,因为iter是字符串,所有不能进行++
*iter.empty();//点运算符优先级比解引用高,所以,对一个地址访问成员是错误的
iter->empty();//相当于(*iter).empty(),对string对象找成员对象是对的
++* iter; //字符串不能++
iter++->empty();//相当于(*iter).empty(),对string对象找成员对象是对的,然后再对iter进行++
}
4.7 条件运算符
练习4.21 编写一段程序,使用条件运算符从vector中找到哪些元素的值是奇数,然后将这些奇数值翻倍
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> a={1,2,3,4};
vector<int> b;
for (int i = 0; i < 4; i++) {
b.push_back((a[i] % 2 != 0) ? (2 * a[i]) : a[i]);
cout << b[i] << endl;
}
}
练习4.22 本节的示例程序将成绩划分成high pass、pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设置为low pass。
要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?
#include<string>
#include<vector>
using namespace std;
int main()
{
//版本一:只使用条件运算符
int socres;
cout << "请输入所得分数:" << endl;
cin >> socres;
string a = ((socres < 60) ? "fail" : (socres <= 75) ? "loe pass" : (socres <= 90) ? "pass" : "high pass");
cout << a << endl;
//版本二:使用1个或多个if语句
cout << "请输入所得分数:" << endl;
cin >> socres;
if (socres < 60) {
cout << "fail" << endl;
}
else if (socres <= 75) {
cout << "low pass" << endl;
}
else if (socres < 90) {
cout << "pass" << endl;
}
else {
cout << "high pass" << endl;
}
}
练习4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
#include<vector>
#include<string>
using namespace std;
int main()
{
string s = "word";
//string p1 = s + s[s.size() - 1] == 's' ? "" : "s"; //原意是想判断s[s.size()-1]是不是等于s,要是是输出"",要是不是,输出s
string p1 = s + ((s[s.size() - 1] == 's') ? "" : "s"); //修改后
}
练习4.24 本节的示例程序将成绩划分成high pass、pass和fail,它的依据是条件运算符满足右结合律。
假如条件运算符满足的是左结合律,求值过程将会是怎么样的?
那就比如string a = ((socres < 60) ? “fail” :(socres <= 75) ? “loe pass” : (socres <= 90) ? “pass” : “high pass”);
改成string a = ((socres >90) ? “fail” : (socres >75) ? “loe pass” :(socres >60) ? “pass” : “high pass”);
4.8 位运算符
对于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型
练习4.25 如果一台机器int占32位、char占8位,用的是Latin-1字符串,其中’q‘的二进制形式是01110001,那么表达式~‘q’<<6的值是什么?
首先对q取反则是10001110,再向左移6位,则为10000000,是2的7次方=128
练习4.26 在本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况?
超过16位的同学,就算通过了测验也是0,会出现位数不够的情况
/练习4.27 下列表达式的结果是什么?
#include<string>
#include<vector>
using namespace std;
int main()
{
unsigned long u11 = 3, u12 = 7;
int a = u11 & u12; //00000011和00000111位与操作,得到00000011,故a=3
int b = u11 | u12; //00000011和00000111位或操作,得到00000111,故b=7
int c = u11 && u12; //执行与判断,由于两个都不等于0,故c=1
int d = u11 || u12; //执行或判断,由于两个都不等于0,故d=1
cout << a << " " << b << " " << c << " " << d << endl;
}
4.9 sizeof运算符
sizeof的返回值是一个常量表达式,所以我们可以用sizeof的结果声明数组的维度
练习4.28 编写一段程序,输出每一种内置类型所占空间的大小
using namespace std;
int main()
{
cout << "void: nullptr_t" << sizeof(std::nullptr_t) << " bytes" << endl;
cout << "bool:" << sizeof(bool) << " bytes" << endl;
cout << "char:" << sizeof(char) << " bytes" << endl;
cout << "wchar_t:" << sizeof(wchar_t) << " bytes" << endl;
cout << "char16_t:" << sizeof(char16_t) << " bytes" << endl;
cout << "char32_t:" << sizeof(char32_t) << " bytes" << endl;
cout << "short:" << sizeof(short) << " bytes" << endl;
cout << "int:" << sizeof(int) << " bytes" << endl;
cout << "long:" << sizeof(long) << " bytes" << endl;
cout << "long long:" << sizeof(long long) << " bytes" << endl;
cout << "float:" << sizeof(float) << " bytes" << endl;
cout << "double:" << sizeof(double) << " bytes" << endl;
cout << "long double:" << sizeof(long double) << " bytes" << endl;
cout << "int8_t:" << sizeof(int8_t) << " bytes" << endl;
cout << "uint8_t:" << sizeof(uint8_t) << " bytes" << endl;
cout << "int16_t:" << sizeof(int16_t) << " bytes" << endl;
cout << "uint16_t:" << sizeof(uint16_t) << " bytes" << endl;
cout << "int32_t:" << sizeof(int32_t) << " bytes" << endl;
cout << "uint32_t:" << sizeof(uint32_t) << " bytes" << endl;
cout << "int64_t:" << sizeof(int64_t) << " bytes" << endl;
cout << "uint64_t:" << sizeof(uint64_t) << " bytes" << endl;
}
练习4.29 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?
#include<string>
using namespace std;
int main()
{
int x[10];
int* p = x;
cout << sizeof(x) / sizeof(*x) << endl; //sizeof(x)=10,sizeof(*x)=1头指针所在
cout << sizeof(p) / sizeof(*p) << endl; //sizeof(p)=2因为地址是int型占2个字节,sizeof(*p)=1头指针所在
}
练习4.30 根据4.12节中的表(第147页),在下述表达式的恰当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同
#include<string>
using namespace std;
int main()
{
(sizeof x) + y;
sizeof(p->men[i]);
(sizeof a) < b;
sizeof(f());
}
4.10 逗号运算符
练习4.31 本节的程序使用了前置版本的递增和递减运算符,解释为什么要用前置版本而不用后置版本;要想用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序
后置版本需要将之前还没进行修改的值保存下来,以便返回,如果不需要未修改的值就使用前置版本。使用后置版本无需改动。
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec = { 1,2,3,4 };
vector<int>::size_type cnt = ivec.size();
for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--) {
ivec[ix] = cnt;
cout << ivec[ix] << endl;
}
}
练习4.32 解释下面这个循环的含义
#include<string>
#include<vector>
using namespace std;
int main()
{
constexpr int size = 5; //定义一个常量表达式
int ia[size] = { 1,2,3,4,5 }; //初始化一个含量为5数组
for (int* ptr = ia, ix = 0; ix != size && ptr != ia + size; ++ix, ++ptr) { //ptr指向ia的首地址,当ix不等于5且ptr所指向地址没到尾部则循环
}
}
练习4.33 根据4.12节中的表(第147页)说明下面这条表达式的含义
someValue ? ++x,++y : --x,–y; 当someValue不等于0时候,x和y各自加一,等于0时候,x和y各自减一
4.11 类型转换
4.11.1 算术转换
练习4.34 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换
需要注意每种运算符遵循的是左结合律还是右结合律
#include<string>
#include<vector>
using namespace std;
int main()
{
float fval;
double dval;
int ival;
char cval;
if (fval); //fval将会转换成bool型
dval = fval + ival; //fval+ival所得结果是float型,赋值给dval后变成了double型
dval + ival * cval; //cval变成int型,最终结果变成double型
}
练习4.35 假设有如下定义
请问下面的表达式中发生了隐式类型转换吗?如果有,请指出来
#include<string>
#include<vector>
using namespace std;
int main()
{
char cval;
int ival = 1;
unsigned int ui = 2;
float fval = 1.1;
double dval;
cval = 'a' + 3; //'a'转换成int型和3相加,之后再转换为char型变成'd'
cout << cval << endl;
fval = ui - ival * 1.0; //ival变成double型,之后再变成unsigned无符号型,最后变成浮点型
cout << fval << endl;
dval = ui * fval; //fval转换成无符号型int,之后相乘结果转换为double型
cout << dval << endl;
cval = ival + fval + dval; //ival转换成了float型与fval相加,之后相加结果变成double型和dval相加最后变成char型
cout << cval << endl;
}
注意:虽然有时候不得不使用强制类型转换,但这种方法本质上是非常危险的
cast-name(expression)
cast-name有static_cast、const_cast(改变表达式的常量属性)、reinterpret_cast(指针对指针使用这个)、dynamic_cast
旧式的强制类型转换
type(expr); 函数形式的强制类型转换
(typr)expr; c语言风格的强制类型转换
4.11.2 其他隐式类型转换
4.11.3 显式转换
练习4.36 假设i是int类型,d是double类型,书写表达式i*=d使其执行整数类型的乘法而非浮点数类型的乘法
#include<string>
#include<vector>
using namespace std;
int main()
{
int i = 10;
double d = 5.5;
i *= static_cast<int>(d);
}
练习4.37 用命名的强制类型转换改写下列旧式的转换语句
#include<string>
#include<vector>
using namespace std;
int main()
{
int i;
double d;
const string* ps;
char* pc;
void* pv;
pv = (void*)ps;
pv = static_cast<void*>(const_cast<string*>(ps)); //新式
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 说明下面这条表达式的含义
#include<string>
#include<vector>
using namespace std;
int main()
{
double slope = static_cast<double>(j / i); //将j除以i的结果转换为double型,赋值给slope
}