4.1.2 节练习
练习4.1
5+10*20/2 = 105
练习4.2
需要在加完括号后运算顺序和加括号前一样一样
a)* vec.begin() *(vec.begin())
b)*vec.begin()+1 (*(vec.begin()))+1
4.1.3节练习
练习4.3
可以接受。如果把运算符求值顺序明确规定,必然会影响程序的效率。但是也有一些潜在的影响,但是大多数的时候程序并不会被这种潜在问题影响,所以只需要在需要的时候,对其进行优化。C++是尽可能相信程序员
4.2节练习
练习4.4
12/3*4+5*15+24%4/2 = (12/3*4)+(5*15)+(24%4/2)=16+75+0=91
练习4.6
(a%2==0)? :
练习4.7
溢出:因为计算机里存取数值都是有位数限制的,有位数限制就会有大小上下限的限制。如果超过了一个数字的上限,那么就会发生溢出。如果比能表示的最小值还要小,就会发生溢出。
比如:
unsigned char x=0xff;
x++;
int a=INT_MAX;
a++;
cout<<a<<endl;
int b=INT_MIN;
b--;
cout<<b<<endl;
练习4.8
逻辑与,当且仅当左侧为真时,才对右侧进行判断
逻辑或,当且仅当左侧为假时,才对右侧进行判断
相等性运算,并未定义求值顺序。不过我试着用一个程序来检测是先运行左边还是右边:
int i=0;
int j=1;
if(i++==(j==i))
cout<<"good";
else
cout<<"bad";
//bad
if((j==i)==i++)
cout<<"good";
else
cout<<"bad";
//good
这个程序说明了我的这个编译器会先执行等号右侧的,然后再执行等号左侧的算式。当然不论先执行哪边,通常情况下都不会影响最终的结果,而且这个式子,最好不要因为执行等号两边的顺序不同,而产生不同的结果。
练习4.9
const char *cp = "Hello World";
if(cp && *cp)
条件部分判断当cp不为空指针时,再去判断这个字符串是否为空串。如果不为空串,则会返回1
练习4.10
#include <iostream>
using std::cin;
using std::cout;
int main() {
int i=0;
while((cin>>i)&&(i!=42)){
cout<<"ok\n";
}
return 0;
}
练习4.11
if(a>b&&b>c&&c>d)
if(a>b>c>d)//这个写法好像也可以通过编译
练习4.12
i!=j<k
该表达式先计算j<k,然后将判断结果用于和i比较,如果判断结果为真,则比较看i是否为1,否则看i是否为0。先计算等号后面的。
i=1;
j=0;
k=0;//如果先计算i!=j,然后用这个结果计算小于k,则判断错误,输出22,否则要是先计算j<K,判断正确,返回11
if(i!=j<k){
cout<<11;
}
else
cout<<22;
总结:
关于比较运算符的前后顺序:
int i=2;
int j=2;
int k=2;
int o=3;
if(i<j<k<o){// 通过数字2223检查比较时是左结合还是右结合。如果为真,说明是左结合,如果条件为假,说明是右结合
cout<<"11111111";
}
关于比较运算符,应该是左结合
关于带不等号的比较符的前后顺序
i=1;
j=0;
k=0;//如果先计算i!=j,然后用这个结果计算小于k,则判断错误,输出22,否则要是先计算j<K,判断正确,返回11
if(i!=j<k){
cout<<11;
}
else
cout<<22;
还有等号的比较符的前后顺序:
int i=0;
int j=1;
if(i++==(j==i))
cout<<"good";
else
cout<<"bad";
//bad
if((j==i)==i++)
cout<<"good";
else
cout<<"bad";
//good
可以看到不论是等于还是不等于,都是先计算等号右边的值,然后用等号左边的值去和这个计算后的右边的值去比较。
练习4.13
a)i=3;d=3;
b)i=3;d=3.5;
练习4.14
第一句无法执行,赋值的左值不允许为一个常量
第二句就是单纯的将42赋值给i,返回值为42,条件为真
练习4.15
因为pi是一个指向int的指针,赋值的类型不同。
修改:dval=ival=*pi=0;
练习4.16
a)会先执行 getPtr()!=0 会将这个判断结果,0 or 1 赋值给p,如果是想要先赋值,那需要括号((p=getPtr())!=0)
b)会直接将1024赋值给i ,如果想要单纯判断i是否为1024,可以(i==1024)
练习4.17
前置递增运算符是先对对象进行递增,然后将递增的结果返回
后置递增运算符也会对对象进行递增,只不过返回的对象是递增前的一个副本
练习4.18
递增运算符的优先级要高于解引用运算符
如果使用前置,那么将会对该元素指向的下一个元素进行运算。因为没法判断下一个元素是否存在,即下一个元素是否<v.end(),所以这样做可能会引起错误。(该循环会从第二个值开始计算,最后会取到v.end().值未定义)
练习4.19
a)判断ptr不为空指针,如果不为空,同时判断ptr指向的值是否为0,同时将ptr指针指向下一个元素
b)ival和ival+1的值是否都不为0
c)判断vec的第ival个元素和ival+1个元素是否相等,但是由于<=运算符,会先计算右侧,所以会导致值永远相等。
修改后:vec[ival]<=vec[ival++];
练习4.20
a)*iter++ 返回一个副本,为iter指向的元素,而当前iter指向下一个元素
b)(*iter)++ 相当于给string+1,这是不合法的
c)*iter.empty() 因为*的优先级低于.的,所以相当于对iter.empty()解引用,iter.empty()是不合法的,通过指针访问对象的成员函数,必须用->
d)iter相当于一个指针,所以可以通过箭头访问成员函数,是看当前元素是否为空
e)不能给一个string 类型++
f)将iter指向下一个元素,但是返回当前的元素的副本的成员函数。
4.7节练习
练习4.21
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
int main() {
vector<int> v{1,2,3,4,5,6,7,8,9};
for(auto iter=v.begin();iter<v.end();iter++){
(*iter)%2==0? :*iter=*iter*2;
}
for(auto iter=v.begin();iter<v.end();iter++)
cout<<(*iter)<<" ";
return 0;
}
练习4.22
只是用条件运算符
#include <iostream>
using std::cin;
using std::cout;
int main() {
int grade;
cin>>grade;
cout<<((grade<60)?"fail":((grade<75)?"low pass":((grade<90)?"pass":"high pass")));
return 0;
}
可以看到程序的可读性很差,逻辑不是一目了然的那种
使用一个或多个if
cin>>grade;
if(grade<60)
cout<<"fail";
else if(grade<75)
cout<<"low pass";
else if(grade<90)
cout<<"pass";
else
cout<<"high pass";
比较两个程序,下面这个相对更加好理解。
练习4.23
string s = "word";
string p1 = s + s[s.size() -1] == 's'?"":"s";
这里是将s和另一个字符的加和作为p1的值,但是加完以后,又去和‘s'进行了比较
可以改为这样:string p1 = s+(s[s.size() -1]=='s'?"":"s");
练习4.24
finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
假如其满足左结合律,等同于 finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";
假如此时 grade > 90
,第一个条件表达式的结果是 "high pass"
,而字符串字面值的类型是 const char *
,非空所以为真。因此第二个条件表达式的结果是 "fail"
。这样就出现了自相矛盾的逻辑
练习4.25
‘q' << 6
得到的结果:7232
练习4.26
有的机器上,unsigned int 是16位的,其结果可能是未定义的。
练习4.27
ul1=3=0b0101
ul2=7=0b0111
a)ul1&ul2 = 0b0101=3
b)ul1|ul2 = 0b0111=7
c)ul1&&ul2 = 1
d)ul1||ul2 = 1
练习4.28
#include <iostream>
using std::cout;
int main() {
cout<<sizeof(int);
cout<<sizeof(double);
cout<<sizeof(long);
cout<<sizeof(char);
cout<<sizeof(unsigned int);
return 0;
}
练习4.29
int x[10];
int*p =x;
cout<<sizeof(x)/sizeof(*x)<<endl; // 10
cout<<sizeof(p)/sizeof(*p)<<endl;//这取决于指针在计算机上的大小,在我的计算机上指针为8字节,所以这个为2
练习4.30
a) sizeof x+y = (sizeof x)+y
b)sizeof p->mem[i] = sizeof(p->mem[i])
c)sizeof a<b = (sizeof a)<b
d)sizeof f() = sizeof(f())
练习4.31
在这里要使用后者版本的递增递减运算符不需要任何改动。
练习4.32
对数组的每个元素进行访问。只是同时使用了两套计数方法。一套是通过index下标,另一套是通过指针来完成。
练习4.33
如果someValue为真,则x 和y 同时加一
如果someValue为假,则x 和y 同时减一 ❌
以上理解错误,没有考虑进去优先级问题,因为逗号优先级最低,所以:
这条表达式等价于:(someValue ? ++x,++y:--x),--y;
所以当条件为真时,x和y同时加一,然后这个式子的返回值是y值,之后会丢弃y值,因为作为第二个逗号,它的左边已经全部计算完成了。此时将这个y丢弃,然后对y进行减一,返回y
当条件为假时,x减一,返回这个x,然后丢弃x,然后对y减一,返回y
int x2=1,y=2;
(1>0)?++x2,++y:--x2,--y;
cout<<x2<<y<<endl;
(1<0)?++x2,++y:--x2,--y;
cout<<x2<<y<<endl;
计算结果:
4.11.1节练习
练习4.34
a)if(fval) float->bool
b)dval = fval+ival ival->float 最终的结果 ->double
c)dval + ival * cval cval->int (ival*cval)->double //右结合律
练习4.35
a)没有❌ 首先‘a' ->int ,然后和3加和的结果转换成char
b) 首先发生了ival -> double 然后 ui ->double 它们的结果double->float
c)ui -> float ;然后它们运算的结果 ->double
d)cval = ival + fval + dval ival->float 它们加和的结果 ->double ;再然后 最终结果 double->char
4.11.3节练习
练习4.36
i*=static_cast<int>(d)
练习4.37
a)pv=(void*)ps;
pv = static_cast<void*>(const_cast<string*>(ps));
b)i=int(*pc)
i = static_cast<int>(*pc)
c)pv = &d
pv = static_cast<void*>(&d)
d)pc=(char*)pv
pc = static_cast<char*>pv;
练习4.38
double slope = static_cast<double>(j/i)
将两个整型相除的结果,还是一个整型强制转换成一个浮点数类型。赋值给slope