C++Primer第五版 第4章 表达式

4.1 基础

4.1.1 基本概念

4.1.2 优先级与结合律

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

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

(a)*vec.begin()
(b)*vec.begin() + 1

答:(a) (vec.begin());解引用得到vec的第一个元素的值
(b) (
(vec.begin())) + 1;

4.1.3 求值顺序

练习 4.3:c++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地,这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
答:不能接受,没有明显的提高代码生成效率,但由此产生的bugs通常难以处理。

4.2 算术运算符

练习 4.4:在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译改(不加括号的)表达式并输出其结果验证之前的推断。
12/34+515+24%4/2

12/3*4+5*15+24%4/2 = ((12/3)*4) + (5*15) + ((24%4)/2) = 91

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

-30 * 3 + 21 / 5
-30 + 3 * 21 / 5
30 / 3 * 21 % 5
-30 / 3 * 21 % 4

答:(a) -90+4 = -86;
(b) -30+63/5 = -30+12 = -18;
© 1021%5 = 210%5 = 0;
(d) -10
21%4 = -210%4 = -2;

练习 4.6:写出一条表达式用于确定一个整数是奇数还是偶数
答:if(num%2 == 0) cout << "num是偶数" << endl; else cout << "num是奇数" << endl;

练习 4.7:溢出是何含义?写出三条将导致溢出的表达式。
答:一部分原因是数学性质本身:例如除数是0的情况;另外一部分则源于计算机的特点:计算的结果超出该类型所能表示的范围时就会产生溢出。

short svalue = 32767; ++svalue; // -32768
unsigned uivalue = 0; --uivalue;  // 4294967295
unsigned short usvalue = 65535; ++usvalue;  // 0

4.3 逻辑和关系运算符

练习 4.8:说明逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。
答:逻辑与中,当且仅当左侧运算对象的结果为真时,才计算右侧运算对象的结果;
逻辑或中,当且仅当左侧运算对象的结果为假时,才计算右侧运算对象的结果;
相等性运算符,没有规定运算对象的求值顺序。
练习 4.9:解释在下面的if语句中条件部分的判断部分。
const char *cp = "Hello World"; if(cp && *cp)
答:cp的类型是const char * ,cp指向(字面值)字符数组,非空,cp为真;*cp的类型是const char,解引用为字符数组的首元素’H’,非0值,为真;逻辑与两侧结果都为真,最终结果为真。
练习 4.10:为while循环写一个条件,使其从标准输入中读入整数,遇到42时停止。
答:int num = 0; while(cin >> num && num != 42)
练习 4.11:书写一条表达式用于测试4个值a,b,c,d的关系,确保a大于b、b大于c、c大于d。
答:if(a>b && b>c && c>d)
练习 4.12:假设i,j,k是三个整数,说明表达式 i != j < k 的含义。
答:<的优先级高于!=;i != ( j < k )

4.4 赋值运算符

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

int i; double d;
(a) d = i = 3.5;
(b) i = d = 3.5;

答:(a) d = 3.0; i = 3; (b) i = 3; d = 3.5;

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

if(42 = i) // ...
if(i = 42) // ...

答:第一行语句会出错,赋值运算的左侧运算对象必须是一个左值;
第二行语句为真,进行赋值运算后,i > 0。

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

double dval; int ival; int *pi;
dval = ival = pi = 0;

答:pi的类型和ival不同,而且pi的类型(int *)无法转换成ival的类型(int),所以尽管0可以赋值给pi,但是pi赋值给ival的语句非法;
修改:dval = ival = 0; pi = 0; 虽然ival和dval的类型不同,但是ival的类型可以转换成double,从而赋值给dval。

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

(a) if(p = getPtr() != 0)
(b) if(i = 1024)

答:(a) 赋值运算的优先级低于逻辑运算,所以会先运行getPtr() != 0,在把结果(1或0)赋值给p;修改为:if((p = getPtr()) != 0)
(b) 语句为真,进行赋值运算后,i > 0;修改为:if(i == 0)

4.5 递增和递减运算符

练习4.17:说明前置递增运算符和后置递增运算符的区别。
答:前置递增运算符 1. 首先将运算对象加1,然后将改变后的对象作为求值结果;2.前置版本将对象作为左值返回;
后置递增运算符 1. 也会将运算对象加1,但是求值结果是运算对象改变之前那个值的副本;2.后置版本则返回对象原始值的副本作为右值返回。
练习4.18:如果第132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?
答:无法输出第一个元素,而且更糟糕的是如果序列中没有负值,程序将可能试图解引用一个根本不存在的元素(pbeg指向vector对象中最后一个元素时)。
练习4.19:假设ptr的类型是指向int的指针、vec的类型是vector、ival的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?

(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]

答:(a) ptr != 0 && *ptr++; 检查指针是否是空指针,并且检查指针所指向的值是否为真;
(b) ival++ && ival; 检查ival的值是否为真,并且检查ival+1的值是否为真;
© vec[ival++] <= vec[ival]; 不正确,修改:vec[ival] <= vec[ival+1]

4.6 成员访问运算符

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

(a)*iter++; (b)(*iter)++; (c)*iter.empty(); 
(d)iter->empty(); (e)++*iter; (f)iter++->empty();

答:(a)*iter++; 合法,后置递增运算符返回原iter的副本,在对其解引用,得到原iter指向的值;再对迭代器加1;相当于 *iter;++iter;
(b)(iter)++; 不合法,对iter解引用,得到iter指向的值,但是iter的类型是string,不能进行加1;
©*iter.empty(); 不合法,本语句试图访问iter的empty成员,但是iter的类型为vector::iterator,不包含任何成员;
(d)iter->empty(); 合法,通过箭头运算符访问成员;
(e)++*iter; 不合法,*iter的类型是string,不能进行加1;
(f)iter+±>empty();合法,首先通过箭头运算符访问成员(iter->empty());再对迭代器加1;相当于 iter->empty();++iter;

4.7 条件运算符

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

#include <iostream>
#include <vector>
using namespace std;
int main(){
	vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
	for( int &num : vec){
		num = ((num & 1 == 0) ? num : num*2);
		cout << num << " ";
	} 
	return 0;
}

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

//版本1:使用条件运算符
finalgrade = (grade > 90) ? "high pass" 
						  : (grade > 75) ? "pass" 
						  : (grade > 60) ? "low pass" : "fail";
//版本2:使用if语句
if(grade > 90) finalgrade = "high pass";
else if(grade > 75) finalgrade = "pass";
else if(grade > 60) finalgrade = "low pass";
else finalgrade = "fail";

答:使用if语句的版本更容易理解,代码的可读性更高。

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

string s = "word";
string pl = s + s.[s.size()-1] == '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” : “pass”;
如果grade>90,第一个条件运算符结果为high pass;字符串不为空,第二个条件运算的结果是fail;
如果grade<90,第一个条件运算符结果为grade < 60的结果;如果grade < 60;第二个条件运算的结果是fail;否则为pass

//最终结果为:
grade > 90;finalgrade = "fail";
grade < 90 && grade > 60;finalgrade = "fail";
grade < 60;finalgrade = "pass";

4.8 位运算符

练习4.25:如果一台机器上int占32位、char占8位,用的是Latin-1字符集,其中字符’q’的二进制形式是01110001,那么表达式~‘q’<<6的值是什么?
答:~‘q’<<6 = ~(00000000 00000000 00000000 01110001)<<6 = 11111111 11111111 11111111 10001110 << 6 = 11111111 11111111 1110 0011 1000 0000 = -7296;

练习4.26:在本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况?
答:“unsigned int”的最小范围是0到65535。由于某些实现只使用“unsigned int”的最小16位,这可能会导致未定义的行为。

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

unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2 (b) ul1 | ul2 (c) ul1 && ul2 (d) ul1 || ul2

答:(a) ul1 & ul2; 结果:0000 0011 & 0000 0111 = 0000 0011 = 3;
(b) ul1 | ul2; 结果:0000 0011 & 0000 0111 = 0000 0111 = 7;
© ul1 && ul2; 结果:真
(d) ul1 || ul2; 结果:真

4.9 sizeof运算符

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

#include <iostream> // high level input/output operations.
using namespace std;
int main()
{   
    cout << "void: nullptr_t\t" << sizeof(std::nullptr_t) << " bytes" << endl << endl;

    cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;

    cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
    cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
    cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
    cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;

    cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
    cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
    cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
    cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;
 
    cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
    cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
    cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;

    cout << "int8_t:\t\t" << sizeof(int8_t) << " bytes" << endl;
    cout << "uint8_t:\t" << sizeof(uint8_t) << " bytes" << endl;
    cout << "int16_t:\t" << sizeof(int16_t) << " bytes" << endl;
    cout << "uint16_t:\t" << sizeof(uint16_t) << " bytes" << endl;
    cout << "int32_t:\t" << sizeof(int32_t) << " bytes" << endl;
    cout << "uint32_t:\t" << sizeof(uint32_t) << " bytes" << endl;
    cout << "int64_t:\t" << sizeof(int64_t) << " bytes" << endl;
    cout << "uint64_t:\t" << sizeof(uint64_t) << " bytes" << endl;
    	
    return 0;
}

练习4.29:推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?

int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(p)/sizeof(*p) << endl;

答:不一样,第一行语句返回数组元素数量,为10;第二行语句含义是:指针的大小/int类型的大小;没有意义。

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

(a) sizeof x+y (b) sizeof p->mem[i] (c) sizeof a<b (d) sizeof f()

答:(a) sizeof x+y ;修改后:(sizeof x)+y;sizeof的优先级高于+
(b) sizeof p->mem[i] ;修改后:sizeof (p->mem[i])
© sizeof a<b ;修改后:sizeof (a)<b
(d) sizeof f();如果f()的返回值是void,那么sizeof f()的结果为undefined;否则sizeof f()的结果为f()的返回值类型。

4.10 逗号运算符

练习4.31:本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
答:对于本题来说,前置版本和后置版本没有区别,使用前置版本仅仅是一个好的书写习惯。

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) { /* ... */ }

答:循环遍历数组;ix和ptr起相同的作用。
练习4.33:根据4.12节中的表(第147页)说明下面这条表达式的含义。

someValue ? ++x, ++y : --x, --y

答:由于逗号运算符的优先级最低,因此表达式与以下内容相同:
(someValue ? ++x,++y : --x) , --y
如果someValue为true,则为"++x,++y",然后在执行"–y",结果为"y";如果someValue是false,则为"–x",结果为"–y"。因此它也与:
someValue?(++x,y)😦–x,–y);
尽管结果与"x"无关,但"someValue"的求值确实会影响对"x"的操作。

4.11 类型转换

4.11.1 算术转换

练习4.34:根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换:需要注意每种运算符遵循的是左结合律还是右结合律。

(a) if (fval) (b) dval = fval + ival; (c) dval + ival * cval;

答:float fval; double dval; int ival; char cval;
(a)fval转换成bool类型; (b) ival转换成float类型,加法运算后的结果转换成double类型,在赋值给dval; © cval转换成int类型,乘法运算后的结果转换成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) cval = ‘a’ + 3; // ‘a’ 转换成 int 类型,然后(‘a’+ 3)的结果转换成char类型;
(b) fval = ui - ival * 1.0; // ival 转换成 double类型 , ui 也转换成 double类型,最后的结果转换成float类型;
© dval = ui * fval; // ui 转换成float类型,然后乘法运算的结果转换成double类型;
(d) cval = ival + fval + dval; //ival转换成float类型,运算ival+fval,结果转换成double类型,在和dval相加,最后的结果转换成char类型。

4.11.2 其他隐式类型转换

4.11.3 显式转换

练习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 = const_cast<string*>(ps)
(b)i = static_cast(pc);
©pv = static_cast<void
>(&d);
(d)pc = static_cast<char*>(pv);

练习4.38:说明下面这条表达式的含义。

double slope = static_cast<double>(j/i);

答:j/i的结果为int类型,static_cast将结果转换为double类型,在赋值给slope。

4.12 运算符优先级

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C Primer》是一本针对C语言初学者的经典教材,第五版的第六主要介绍了函数的基本概念和用法。 在第六中,教材首先介绍了函数的定义和调用。函数是具有独立功能的代码块,可以通过函数名来调用。函数由函数头、参数、函数体和返回值组成。函数头包括函数类型、函数名和参数类型,参数用于接收传递给函数的值,返回值用于将结果返回给调用者。 接着,教材详细介绍了函数的参数传递方式,包括按值传递、按引用传递和按指针传递。按值传递是指将参数的值复制给形参,按引用传递是指将参数的引用传递给形参,而按指针传递是将参数的地址传递给形参。不同的传递方式在函数内部对参数进行操作时,会对应不同的结果。 此外,教材还讲解了函数的返回值和函数的调用顺序。函数的返回值类型由函数头中的类型确定,可以是任意类型。当函数需要返回多个值时,可以使用结构体或指针进行返回。函数的调用顺序决定了函数执行的顺序,即哪个函数先执行,哪个函数后执行。 在函数的实现过程中,教材介绍了函数的定义和声明、局部变量和全局变量、递归等内容。通过这些知识点的学习,读者可以了解到函数的具体细节和一些实践技巧。 总的来说,第五版的第六通过清晰的语言和丰富的例子,循序渐进地讲解了函数的基本概念和用法。学完这一,读者将能够理解函数的定义、调用、参数传递、返回值和实现等方面的知识,为之后的学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值