C++(8)表达式

表达式--算术、关系、逻辑、位、赋值、自增/自减操作符

引:

除了特殊用法,表达式的结果为右值:可以读取结果值,但是不能对其进行赋值。

高优先级的操作符要比低优先级的结合得更紧密。

正文:

1、某些算术表达式的求解结果未定义:一部分有数学特性决定,如除0操作;另一部分则归咎于计算机特性,如溢出。

2、除法和求模操作:

1)如果两个操作数都是负数:则除法操作的结果为正数,而求模操作结果为负数

2)如果只有一个操作数是负数:这两种操作的结果都要取决于机器:求模结果的符号要取决于机器,除法操作的值则是负数。

[cpp] view
plain
copy

  1. cout << -21 % -8 << endl;
  2. cout << 21 % -5 << endl;    //1 or -4
  3. cout << -21 / -8 << endl;
  4. cout << 21 / -5 << endl;    //-4 or -5

3、关系操作符和逻辑操作符的返回结果都是bool类型的值

短路求值-逻辑与&&与逻辑或||,都是只有仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。

4、一个&&操作符的使用技巧:如果某边界条件使得expr2的计算变得非常危险,那么应该在该条件出现之前,先让expr1成为false。

[cpp] view
plain
copy

  1. string s("Expressions in C++ are composed...");
  2. string::iterator iter = s.begin();
  3. while (iter != s.end() && ! isspace(*iter))
  4. {
  5. *iter = toupper(*iter);
  6. ++ iter;
  7. }
  8. cout << s << endl;

5、如果逻辑非的操作数为非零值,则!操作的结果为false。

6、不应该串接使用关系操作符,如:

[cpp] view
plain
copy

  1. if (i < j < k)
  2. {
  3. //...
  4. }

该表达式的结果往往是出人意料的:这种写法只要k值大于1,则该表达式的结果就为true;

[cpp] view
plain
copy

  1. //P133 习题5.7
  2. int main()
  3. {
  4. int val;
  5. while (cin >> val && val != 42)
  6. {
  7. //...
  8. }
  9. }

[cpp] view
plain
copy

  1. //习题 5.8
  2. if (a > b && b > c && c > d) {//...}

7、位操作符可以将整型操作符视为二进制位的集合,而且还可以作用于bitset类型。

8、如果位操作符的操作数为负数,则位操作符如何处理其操作数的符号要依赖于机器。因此,强烈建议使用unsigned整型操作数。

1<< n :相当于1* 2^n

m<< n :相当于m* 2^n

9、bitset对象与整型值的使用对比

[cpp] view
plain
copy

  1. //bitset对象的每一位都默认为0
  2. bitset<30> bitset_quizl;
  3. unsigned long int_quizl = 0;
  4. //设置第27位为1
  5. bitset_quizl.set(27);
  6. int_quizl |= 1UL << 27;
  7. //设置第27位为0
  8. bitset_quizl.reset(27);
  9. int_quizl &= ~(1UL << 27);
  10. //获得第27位上的取值
  11. bool status;
  12. status = bitset_quizl[27];
  13. status = int_quizl & (1UL << 27);

通常来说:bitset优于整型数据的低级直接位操作。

[cpp] view
plain
copy

  1. //P136 习题5.9
  2. int main()
  3. {
  4. unsigned int ul1 = 3,ul2 = 7;
  5. cout << (ul1 & ul2) << endl;
  6. cout << (ul1 | ul2) << endl;
  7. cout << (ul1 && ul2) << endl;
  8. cout << (ul1 || ul2) << endl;
  9. }

[cpp] view
plain
copy

  1. //习题5.10
  2. int main()
  3. {
  4. bitset<30> bitset_quizl;
  5. //设置第27位为1
  6. bitset_quizl[27] = 1;
  7. cout << bitset_quizl << endl;
  8. //设置第27位为0
  9. bitset_quizl[27] = 0;
  10. cout << bitset_quizl << endl;
  11. cout << bitset_quizl[27] << endl;
  12. }

10、移位操作符用于I/O

移位操作符具有中等优先级:其优先级比算术运算符低,但比(1)关系操作符,(2)赋值操作符,(3)条件运算符优先级高。

11、赋值运算符具有右结合性,如:

[cpp] view
plain
copy

  1. int ival1,ival2;
  2. ival1 = ival2 = 0;
  3. //等价与
  4. (ival1 = (ival2 = 0));

[cpp] view
plain
copy

  1. //P139 习题5.11 i、d的值分别为?
  2. int i;
  3. double d;
  4. d = i = 3.5;
  5. i = d = 3.5;

[cpp] view
plain
copy

  1. //习题5.12 下面程序会有什么结果
  2. //(1)
  3. int main()
  4. {
  5. int i;
  6. if (42 = i)
  7. {
  8. //...
  9. }
  10. }

[cpp] view
plain
copy

  1. //(2)
  2. int main()
  3. {
  4. int i;
  5. if (i = 42)
  6. {
  7. //...
  8. }
  9. }

[cpp] view
plain
copy

  1. //P140 习题5.14 下面程序会有什么输出结果
  2. int main()
  3. {
  4. int ival = 2;
  5. ival += (ival + 1);
  6. cout << ival << endl;
  7. }

12、建议:只有在必要时才使用后置自增/自减操作符

前置操作符需要做的工作更少,而后置操作符必须先保存操作数原来的值,以便返回未加/减1之前的结果。【P140建议...值得仔细品读!】

13、后自增操作符的优先级高于解引用操作符,因此*iter++等价与*(iter++).

箭头、条件、sizeof、逗号表达式与复合表达式求值

1、C++为包含点操作符和解引用操作符的表达式提供了一个同义词:箭头操作符(->)

[cpp] view
plain
copy

  1. Sales_item *sp = &item;
  2. (*sp).same_isbn(item_other);
  3. sp -> same_isbn(item_other); //与上一条语句相同

[cpp] view
plain
copy

  1. //P142 习题5.18
  2. int main()
  3. {
  4. vector<string *> strVec;
  5. string value;
  6. while (cin >> value)
  7. {
  8. string *pstr = new string;
  9. *pstr = value;
  10. strVec.push_back(pstr);
  11. }
  12. for (vector<string *>::iterator iter = strVec.begin(); iter != strVec.end(); ++iter)
  13. {
  14. cout << **iter << " size: " << (**iter).size() << endl;
  15. }
  16. for (vector<string *>::iterator iter = strVec.begin(); iter != strVec.end(); ++iter)
  17. {
  18. delete *iter;
  19. }
  20. return 0;
  21. }

2、条件表达式的优先级相当低,通常都需要用圆括号将表达式扩起来

[cpp] view
plain
copy

  1. cout << (i < j ? i : j) << endl;
  2. cout << (i < j) ? i : j << endl; //ERROR
  3. cout << i < j ? i : j << endl;       //ERROR

[cpp] view
plain
copy

  1. //P144 习题5.21
  2. int main()
  3. {
  4. vector<int> ivec;
  5. int value;
  6. freopen("input.txt","r",stdin);
  7. while (cin >> value)
  8. {
  9. ivec.push_back(value);
  10. }
  11. for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
  12. {
  13. *iter = (*iter % 2 ? *iter * 2 : *iter);
  14. cout << *iter << ‘ ‘;
  15. }
  16. cout << endl;
  17. }

3、sizeof操作符

sizeof操作符返回一个对象或类型名的长度,返回值类型为size_t,长度单位为字节。

sizeof表达式的结果是编译时常量。

[cpp] view
plain
copy

  1. Sales_item item,*pi;
  2. cout << sizeof(Sales_item) <<  endl;
  3. cout << sizeof item << endl;
  4. cout << endl;
  5. cout << sizeof(pi) << endl;
  6. cout << sizeof(*pi) << endl;
  7. cout << endl;
  8. cout << sizeof(9 + 8) << endl;
  9. cout << endl;
  10. char *p = new char[50];
  11. cout << sizeof(p) << endl;
  12. cout << sizeof(*p) << endl;
  13. cout << endl;
  14. char arr[50];
  15. cout << sizeof(arr) << endl;
  16. cout << sizeof(*arr) << endl;
  17. cout << sizeof(char) << endl;
  18. cout << endl;
  19. Sales_item &refiterm = item;
  20. cout << sizeof(refiterm) << endl;

4、逗号操作符

逗号表达式是一组由逗号分割的表达式,这些表达式从左到右进行计算。然而,逗号表达式的结果是其最右边表达式的值。

5、复合表达式中优先级和结合性决定了表达式的哪个部分用作哪个操作符的操作数,但是还可以通过使用圆括号强制实现某个特殊的分组,因为圆括号凌驾于优先级之上。

6、求值顺序

除了&&、||与?:运算符外,未指定操作符的运算顺序。

当操作符的两个操作数涉及到同一个对象,并改变其值时,操作数的计算顺序才会影响结果。

C++并不保证计算是从左到右的!

【P149建议:复合表达式的处理值得一读!】

在一个表达式里,不要在两个或更多的子表达式中对同一个对象做自增或自减操作。

new/delete表达式与类型转换

1、在创建对象时必须指定其类型与名称,而创建动态对象时,程序指定其数据类型而不必为对象命名;new表达式返回指向新对象的指针,我们通过此指针访问该对象。

[cpp] view
plain
copy

  1. int *p = new int;

此new表达式在自由存储区中分配创建了一个整型对象,并返回该对象的地址,并用该地址初始化指针。

2、动态对象的初始化

[cpp] view
plain
copy

  1. int *pi = new int(65535);
  2. string *ps = new string(9,‘H‘);

3、动态对象的默认初始化方式与在函数内定义的变量的初始化方式相同。如内置类型并不进行初始化...

但是,对于一个动态创建的对象,(几乎)总是对它做初始化也是一个好办法。

[cpp] view
plain
copy

  1. string *ps = new string();
  2. int *pi = new int();

但是,对于提供了默认构造函数的类类型,其实没有必要对其对象进行值初始化,而对于内置类型或没有定义默认构造函数的类类型,采用不同初始化方式则有明显的不同:

[cpp] view
plain
copy

  1. int *pi = new int;  //没有初始化
  2. int *ip = new int();    //初始化为0

4、动态创建的对象用完之后,一定要显式的将该对象的内存返回给自由存储区:

[cpp] view
plain
copy

  1. delete pi;

但是如果指针指向的不是由new分配的内存地址,则在该地址上使用delete是不合法的,但是编译器是不容易察觉出的:

[cpp] view
plain
copy

  1. int i;
  2. int *pi = &i;
  3. delete pi;  //ERROR,但是在大多数编译器上能够通过!小心!
  4. string str = "Hi~";
  5. delete str; //ERROE

5、在执行了语句:

[cpp] view
plain
copy

  1. delete p;

之后,尽管p值没有了明确定义,但是仍然存放了它之前的所指向的对象的指针,因此,一旦释放了指针所指向的值,立即将指针设置成为0,这样就非常清楚的表明该指针不再指向任何对象。

[cpp] view
plain
copy

  1. int *p = new int;
  2. delete p;
  3. p = 0;

6、C++允许创建const对象:

[cpp] view
plain
copy

  1. const int *pci = new const int(1024);

动态创建的const对象必须在创建时初始化,并且一旦初始化,其值就不能再修改;如果对于类类型的const动态对象,该类类型提供了默认的构造函数,则此对象可以隐式的初始化:

[cpp] view
plain
copy

  1. const string *pcs = new const string;

删除const对象【与普通对象相同】:

[cpp] view
plain
copy

  1. delete pcs;

7、动态内存管理的常见错误

1)删除(delete)指向动态分配内存的指针失败,因而无法将该内存返还给自由存储区,通常称为“内存泄漏(memoryleak)”,这类错误很难发现,需要直到所有内存耗尽,内存泄漏才会显露出来!

2)读写已经删除的对象。如果释放了指针所指向的对象之后,将指针置为0值,则比较容易检测出这类错误。

3)对同一个内存空间delete两次。当两个指针执行同一个动态创建的对象,删除时就发生错误,此时自由存储区可能会被破坏!

【P153警告:~非常精彩,值得仔细品读!!!】

[cpp] view
plain
copy

  1. //P153 习题5.30
  2. vector<string> svec(10);
  3. vector<string> *pvec1 = new vector<string>(10);
  4. vector<string> **pvec2 = new vector<string>[10];    //ERROE
  5. vector<string> *pv1 = &svec;
  6. vector<string> *pv2 = pvec1;
  7. delete svec;    //EEEOR
  8. delete pvec1;
  9. delete []pvec2; //ERROR
  10. delete pv1;     //ERROR,但是编译器有可能检测不出
  11. delete pv2;     //ERROR,但是编译器有可能检测不出

8、C++定义了算术类型之间的内置转换以尽可能防止精度损失,如算术转换:保证在执行操作之前,将二元操作符的两个操作数转换为同一类型,并使表达式的值也有相同的类型。

【注意:】对于signed和unsignedint型的表达式,转换非常虐心...

[cpp] view
plain
copy

  1. unsigned int i = 5;
  2. int j = -6;
  3. cout << i + j << endl;

程序的输出结果为:4294967295【2^32– 1,sizeof(i+ j) = 4】,表达式中的signed类型值会被转换为unsigned类型,并使i+j也拥有unsigned特性!

9、在大多数情况下,数组都会转换成为指向第一个元素的指针,但是也有例外情况:

1)数组用作取地址操作符的操作数;

2)sizeof操作符的操作数;

3)用数组对数组的引用进行初始化时.

10、另外两种指针间的转换:

1)指向任意类型的指针都可以转换为void*类型;

2)整型数值0可以转换为任意指针类型。

11、C++自动将枚举类型的对象或枚举成员转换成为整型

[cpp] view
plain
copy

  1. enum Points {point2d = 2,point2w,
  2. point3d = 3,point3w
  3. };
  4. const size_t arr_size = 1024;
  5. Points tmp = point3d;
  6. int chunk_size = arr_size * point2w;
  7. int arr_3d = chunk_size * tmp;
  8. cout << arr_3d << endl;

12、显式转换/强制类型转换:static_cast,dynamic_case,const_cast,reinterpret_cast

13、const_cast,转换掉表达式的const性质

[cpp] view
plain
copy

  1. char *string_copy(char *temp)
  2. {
  3. return temp;
  4. }
  5. int main()
  6. {
  7. const char *pc_str = "Hello";
  8. char *pc = string_copy(pc_str);             //ERROR
  9. char *pc = string_copy(const_cast<char *>(pc_str));
  10. }

14、编译器隐式执行的任何类型转换都可以由static_cast显式完成:

[cpp] view
plain
copy

  1. double d = 97.00;
  2. char ch = static_cast<char>(d);
  3. //找回存放在void *指针中的值
  4. void *p = &d;
  5. double *dp = static_cast<double *>(p);

该语句可以替换任何旧式强制类型转换【下文】!

15、reinterpret_cast通常为操作数的位模式提供较低层次的重新解释,如:

[cpp] view
plain
copy

  1. int *ip;
  2. char *pc = reinterpret_cast<char *>(ip);
  3. string str(pc);     //此处编译器并未报错

编译器确实无法知道pc实际上指向的是int型的对象的指针,所以用pc初始化str的值是完全正确的– 虽然实际上是无意义的或是错误的。

[cpp] view
plain
copy

  1. //执行下面程序
  2. int main()
  3. {
  4. int i = 1024;
  5. int *ip = &i;
  6. char *pc = reinterpret_cast<char *>(ip);
  7. string str(pc);
  8. cout << str << endl;
  9. }

建议:避免使用强制类型转换!【P160】

16、旧式强制类型转换

虽然标准C++仍然支持旧式的强制类型转换标号,但是我们建议:只有在C或者标准C++之前的编译器上使用这种语法!

[cpp] view
plain
copy

  1. //P161 习题5.33
  2. int ival;
  3. double dval;
  4. const string *ps;
  5. char *pc;
  6. void *pv;
  7. pv = static_cast<void *>(pc);
  8. ival = static_cast<int>(*pc);
  9. pv = static_cast<void *>(&dval);
  10. pc = static_cast<char *>(pv);

本文借鉴:http://blog.csdn.net/column/details/zjf666.html?&page=5

版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合

时间: 2024-10-16 22:28:58

C++(8)表达式的相关文章

.NET深入解析LINQ框架(六:LINQ执行表达式)

阅读目录: 1.LINQ执行表达式 在看本篇文章之前我假设您已经具备我之前分析的一些原理知识,因为这章所要讲的内容是建立在之前的一系列知识点之上的,为了保证您的阅读顺利建议您先阅读本人的LINQ系列文章的前几篇或者您已经具备比较深入的LINQ原理知识体系,防止耽误您的宝贵时间. 到目前为止我们对LINQ的执行原理已经很清楚了,从它的前期构想到它真正为我们所用都有足够的证据,但是似乎问题并没有我们想的那么简单,问题总是在我们使用中频频出现尤其是新技术的使用,当然有问题才能有进步. 一:LINQ执行

Lambda表达式实战视频教程

视频教程地址: https://my.oschina.net/u/3217554/blog/1507456 1:Lambda表达式及函数式接口介绍 2:Lambda表达式详解 3:方法的引用(一) 4:方法的引用(二) 5:Stream API(一) 6:Stream API(二) 7:Lambda表达式.方法的引用.Stream API实战

lambda表达式封装对数据库的查询

前言: 1.为什么要封装lambda表达式数据库查询,原因有一下几点: 1.1.在以往的开发中进行数据库表查询时,其实所需要的字段就是其中几个,但是在开发中,开发者往往习惯select * 进行查询,当数据多和用户量多时,查询的效率会降低. 1.2.在写查询where条件的时候,总是用string.format去拼接字符串,开发效率低. 1.3.代码不够优雅,代码中嵌套和多sql语句,如果是表字段发生改变时编译器检查不出来,代码出错的概率大. 1.4.本着 write less  do more

坑爹的EL 表达式。

先说结论: Property 'OrgName0' not found on type EL表达式只与相关类的get方法的有关,就算你没定义变量,get 方法直接返回一个变量,或者常量都可以.这条没问题. EL表达式的大小写规则:当get方法后的字符如果前两个都是大写时,EL表达式需要大写.除此之外均为小写. package com.dobs.codedeploy.domain; public class Image { private String imageName;private Stri

Spring AOP中pointcut expression表达式解析 及匹配多个条件

Pointcut 是指那些方法需要被执行"AOP",是由"Pointcut Expression"来描述的. Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合. args() @args() execution() this() target() @target() within() @within() @annotation 其中 execution 是用的最多的,其格式为: execution(modifiers-pat

Lambda表达式

import org.junit.Test; import java.util.Comparator; import java.util.function.Consumer; /** * 一.Lambda 表达式基础语法:Java8中引入一个新的操作符"->"该操作符称为箭头操作符或Lambda操作符 * 箭头操作符将Lambda表达式拆分为两部分: * 左侧: Lambda表达式的参数列表 * 右侧: Lambda表达式中所需要执行的功能,即Lambda体 * * 语法格式一:

算法训练 表达式计算

问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 -4 数据规模和约定 表达式长度不超过100,表达式运算合法且运算过程都在int内进行. import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.uti

JAVAWEB开发之Session的追踪创建和销毁、JSP详解(指令,标签,内置对象,动作即转发和包含)、JavaBean及内省技术以及EL表达式获取内容的使用

Session的追踪技术 已知Session是利用cookie机制的服务器端技术,当客户端第一次访问资源时 如果调用request.getSession() 就会在服务器端创建一个由浏览器独享的session空间,并分配一个唯一且名称为JSESSIONID的cookie发送到浏览器端,如果浏览器没有禁用cookie的话,当浏览器再次访问项目中的Servlet程序时会将JSESSIONID带着,这时JSESSIONID就像唯一的一把钥匙  开启服务器端对应的session空间,进而获取到sessi

正则 常用表达式

说明 正则表达式 网址(URL) [a-zA-z]+://[^\s]* IP地址(IP Address) ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) 电子邮件(Email) \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* QQ号码 [1-9]\d{4,} HTML标记(包含内容或自闭合) <(.*)(.*)>.*<\/\1>|<(.*) \/>

lamda表达式

"Lambda 表达式"(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数.Lambda表达式可以表示闭包(注意和数学传统意义上的不同). 匿名函数:匿名函数不以文件形式驻留在文件夹上:他的生成方式最简捷,可在指令窗或任何函数体内通过指令直接生成 闭包:指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何