4.3 C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行权衡,你认为这可以接受吗?请说出你的理由。
对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生为定义行为如:
cout<<i<<" "<<++i<<endl; //不能确定先执行++i还是i
而如果运算对象彼此无关,它们既不会改变同一对象的状态也不执行IO任务,则函数掉用顺序不受限制。所以在一定程度上可以接受,前提是在编写程序时注意:
- 当不确定执行顺序时,最好用括号让表达式的组合关系符合程序逻辑的要求。
- 一旦在一个表达式中改变了某个运算对象的值,在表达式的其他地方就不要再使用该运算对象。
4.7 溢出是何含义?写出三条将导致溢出的表达式
? 溢出即数值超过了给定类型的合法范围
?
4.8说明在逻辑与、逻辑或以及相等性运算符中运算对象的求值顺序
- 相等性运算符(==)没有定义求值顺序
- 逻辑与、逻辑或都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式结果时才会计算右侧运算对象的值。这种策略称为短路求值。
4.9 解释下面if语句中条件部分判断过程
const char *cp = "hello world!";
if(cp && *cp)
先判断cp是否为空指针,若不是空指针,再判断cp[0]是否为结束符(’\0’)
4.12 假设i、j和k是三个整数,说明表达式i !=j<k 的含义
题中表达式等价于(i != j) < k,即先判断i是否不等于j。若i不等于j返回true转换为1,否则返回0。随后再利用该结果与k进行比较。
4.17 说明前置和后置递增运算符区别
前置递增运算符将对象本身加1之后将对象本身作为左值返回
后置递增,将对象本身加1后,将对象原始值副本保存下来作为右值返回
4.18 若ptr是int *类型,vec类型为vector,ival类型为int,说明下面表达式含义,若有表达式不正确,为什么?应该如何修改?
ptr != 0 && *ptr++;//a
ival++ && ival //b
vec[ival++] <= vec[ival] //c
a:先判断ptr是否为空指针,若为空指针,判断*ptr是否为0并且ptr向前移动一个位子(+1),若为0则表达式返回false否则返回true
b:判断ival是否为0并且ival+1,若ival非0则左边运算对象返回true再判断新的ival是否为0,若为0则整个表达式返回false,否则返回true
c:有错误,关系运算符<=左右两边运算对象求值顺序未定,若先求左边对象则表达式转为:
vec[ival] <= vec[ival+1]
若先求右侧表达式,则源表达式变为:
vec[ival] <= vec[ival];
ival++;
根据代码应该改为
vec[ival+1] <= vec[ival];
ival++;
4.20 假设iter的类型是vector::iterator,说明下面表达式是否合法,如果合法表达式含义是什么?如果不合法,错在哪里?
*iter++; //正确,迭代器iter后置递增,并对递增前的迭代器解引用操作
(*iter)++; //对迭代器iter解引用获得string对象,并对其执行++操作,但string对象未定义++运算,故不合法
*iter.empty();//错误,.运算符优先级大于解引用,故先执行iter.empty(),但iter为迭代器没有empty成员函数,故错误
iter->empty(); //调用iter所指向string对象的empty成员函数
++*iter; //错误,先解引用iter得到string对象,但string没有前置递增成员函数
iter++->empty();//调用迭代器iter所指对象的empty()函数,然后将iter指向下一个string对象
4.23 因为运算符优先级问题,下面这条表达式无法通过编译。指出问题,并修改它
string s = "word";
string p1 = s + s[s.size() - 1] == 's'?"":"s";
优先级上+高于==,==高于?: ,所以会先计算s + s[s.size()-1] 得到string对象
但是没有==可以用于string和char。应修改为:
string s = "word";
string p1 = s + (s[s.size() - 1] == 's' ? "" : "s");
4.24 下列表达式若满足左结合律,求值过程应该怎样。
(grade > 90)?"high pass"
:(grade < 60)?"fail":"pass";
若满足做结合律,表达式会被解析为:
((grade > 90)?"high pass":(grade < 60))?"fail":"pass";
假如此时grade > 90 ,第一个条件表达式的结果是"high pass",而字符字面值类型是const char *,非空所以为真。因此判断条件表达式为真,返回fail,矛盾。
4.30 根据P147优先级表,在下述表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来含义相同
sizeof x+y;
sizeof p->mem[i];
sizeof a<b;
sizeof f();
加上括号后如下
(sizeof x)+y; //sizeof 高于 算术运算符
sizeof (p->mem[i]); //sizeof低于->和.运算符
(sizeof a) < b; //sizeof高于关系运算符
sizeof (f()); //sizeof低于函数调用
4.31 本节程序用了前置递增和递减运算符,解释为什么要用前置版本而不用后置版本,要想使用后置版本的递增和递减运算符需做哪些改动?使用后置版本重写本节程序
-
前置版本递增后,将对象本身作为左值返回;而后置版本则将对象原始值的副本作为右值返回,后置版本需要将原始值储存下来以便于返回这个未修改的内容,对于复杂的迭代器类型,这种额外的工作消耗就更大了。所以选择用前置版本递增 -
使用后置版本递增和递减无需改动: vector<int> ivec(9);
vector<int>::size_type cnt = ivec.size();
for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, --cnt)
{
ivec[ix] = cnt;
}
for (int n : ivec)
cout << n << " ";
cout << endl;
4.34 说明在下面表达式中将发生什么样类型转换
if(fval); //若fval为0则转换为false,否则转换为true
//ival转换为float,与fval相加后再转换为double
dval = fval + ival;
//cval转换为int与ival相乘所得结果为int类型,再转换为double与dval相加
dval + ival * cval;
4.35 假设有如下定义
char cval; int ival; unsigned int ui; float fval; double dval;
问在下面表达式中发生了隐式类型转换吗?如果有,请指出。
cval = 'a' + 3;//'a'由char转换为int与3相加然后其结果再隐式类型转换为char
fval = ui - ival * 1.0;//ival先转换为double与1.0相乘得到【结果1】,【结果1】与ui(unsigned int)转换为double得到的内容相减,最后结果再转换为double
dval = ui * fval;//将ui转换为float类型与fval相乘所得结果再转换为double
cval = ival + fval + dval;//将ival转换为float类型与fval相加得结果1,再将结果1转换为double类型与dval相加得到的结果去除小数部分转换为char类型
4.36 用命名的强制类型转换改写下列旧式转换语句
int i;
double d;
const string* ps;
char* pc;
void* pv;
pv = (void*)ps;//C风格强转
i = int(*pc); // 函数式强转
pv = &d;
pc = (char*)pv;
用命名的强制类型转换:
//先const string* -> string* -> void*
pv = reinterpret_cast<void*>(const_cast<string*>(ps));
i = static_cast<int>(*pc);
pv = reinterpret_cast<void*>(&d);
pc = reinterpret_cast<char*>(pv);
|