C++primer 第 4 章 表达式 4.7条件运算符 4.8位运算符 4.9 sizeof运算符 4.10逗号运算符 4.11类型转换 4 . 1 2 运算符优先级表

4.7条件运算符

  • 条件运算符(?:)允许我们把简单的if else逻辑嵌入到单个表达式当中,条件运算符按照如下形式使用:
  • cond ? expr1 : expr2;其中cond是判断条件的表达式,而expr1和expr2是两个类型相同或可能转换为某个公共类型的表达式。条件运算符的执行过程是:首先求cond的值,如果条件为真对expr1求值并返回该值,否则对expr2求值并返回该值。举个例子,我们可以使用条件运算符判断成绩是否合格:
  • string finalgrade = (grade<60) ?"fail" : "pass”;
  • 条件部分判断成绩是否小于60。如果小于,表达式的结果是"fail",否则结果是"pass"。有点类似于逻辑与运算符和逻辑或运算符(&&和||),条件运算符只对expr1和expr2中的一个求值。
  • 当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果是左值;否则运算的结果是右值。

嵌套条件运算符

  • 允许在条件运算符的内部嵌套另外一个条件运算符。也就是说,条件表达式可以作为另外一个条件运算符的cond或expr
  • 举个例子,使用一对嵌套的条件运算符可以将成绩分成三档:优秀(highpass)>合格(pass)和不合格(fail):
  • finalgrade=(grade>90)?"highpassn:(grade<60)?“fail":"pass";
  • 第一个条件检查成绩是否在90分以上,如果是,执行符号?后面的表达式,得到"highpass";如果否,执行符号:后面的分支。这个分支本身又是一个条件表达式,它检查成绩是否在60分以下,如果是,得到"fail";否则得到"pass"。
  • 条件运算符满足右结合律,意味着运算对象(一般)按照从右向左的顺序组合。因此在上面的代码中,靠右边的条件运算(比较成绩是否小于60)构成了靠左边的条件运算的:分支。
  • 随着条件运算嵌套层数的增加,代码的可读性急剧下降.因此,条件运算的嵌套最好别超过两到三层

4.8位运算符

  • 位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。位运算符提供检查和设置二进制位的功能,如17.2节(第640页)将要介绍的,一种名为bitset的标准库类型也可以表示任意大小的二进制位集合,所以位运算符同样能用于bitset类型.

  • 一般来说,如果运算对象是''小整型”,则它的值会被自动提升(参见4.11.1节,第142页)成较大的整数类型。运算对象可以是带符号的,也可以是无符号的。如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖于机器。而且,此时的左移操作可能会改变符号位的值,因此是一种未定义的行为。
  • 关于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型。

移位运算符

  • 之前在处理输入和输出操作时,我们已经使用过标准IO库定义的<<运算符和>>运算符的重载版本。这两种运算符的内置含义是对其运算对象执行基于二进制位的移动操作,首先令左侧运算对象的内容按照右侧运算对象的要求移动指定位数,然后将经过移动的(可能还进行了提升)左侧运算对象的拷贝作为求值结果。其中,右侧的运算对象一定不能为负,而且值必须严格小于结果的位数,否则就会产生未定义的行为。二进制位或者向左移(?)或者向右移(?),移出边界之外的位就被舍弃掉了:

  • 左移运算符(<<)在右侧插入值为0的二进制位。右移运算符(>>)的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位:
  • 如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定。

位求反运算符

  • 位求反运算符( ~) 将运算对象逐位求反后生成一个新值,将 1 置为0、将 0 置为1:
  • char类型的运算对象首先提升成int类型,提升时运算对象原来的位保持不变,往高位(highorderposition)添加0即可。因此在本例中,首先将bits提升成int类型,增加24个高位0,随后将提升后的值逐位求反。

位与、位或、位异或运算符

  • 与(&)、或(|)、异或(^)运算符在两个运算对象上逐位执行相应的逻辑操作:

  • 对于位与运算符(&)来说,如果两个运算对象的对应位置都是1则运算结果中该位为1,否则为0。对于位或运算符(|)来说,如果两个运算对象的对应位置至少有一个为1则运算结果中该位为1,否则为0。对于位异或运算符(^)来说,如果两个运算对象的对应位置有且只有一个为1则运算结果中该位为1,否则为0

移位运算符(又叫10运算符)满足左结合律

  • 尽管很多程序员从未直接用过位运算符,但是几乎所有人都用过它们的重载版本来进行10操作。重载运算符的优先级和结合律都与它的内置版本一样,因此即使程序员用不到移位运算符的内置含义,也仍然有必要理解其优先级和结合律。
  • 因为移位运算符满足左结合律,所以表达式

4.9 sizeof运算符

  • sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,其所得的值是一个size_t类型(参见3.5.2节,第 103页)的常量表达式(参 见2.4.4节,第 58页)。运算符的运算对象有两种形式:
  • sizeof (type)
  • sizeof expr

  • 这些例子中最有趣的一个是sizeof *p。首先,因为sizeof满足右结合律并且与*运算符的优先级一样,所以表达式按照从右向左的顺序组合。也就是说,它等价于sizeof(*p)
  • 其次,因为sizeof不会实际求运算对象的值,所以即使p是一个无效(即未初始化)的指针(参见2.3.2节,第47页)也不会有什么影响。在sizeof的运算对象中解引用一个无效指针仍然是一种安全的行为,因为指针实际上并没有被真正使用。
  • sizeof不需要真的解引用指针也能知道它所指对象的类型。新标准允许我们使用作用域运算符来获取类成员的大小。通常情况下只有通过类的对象才能访问到类的成员,但是sizeof运算符无须我们提供一个具体的对象,因为要想知道类成员的大小无须真的获取该成员。
  • sizeof运算符的结果部分地依赖于其作用的类型:
  • 对char或者类型为char的表达式执行sizeof运算,结果得1。
  • 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
  • 对指针执行sizeof运算得到指针本身所占空间的大小。
  • 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效。
  • 对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理。
  • 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。
  • 因为执行sizeof运算能得到整个数组的大小,所以可以用数组的大小除以单个元素的大小得到数组中元素的个数:

 

4.10逗号运算符

  • 逗号运算符含有两个运算对象,按照从左向右的顺序依次求值。和逻辑与、逻辑或以及条件运算符一样,逗号运算符也规定了运算对象求值的顺序。
  • 对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果丢弃掉。逗号运算符真正的结果是右侧表达式的值。如果右侧运算对象是左值,那么最终的求值结果也是左值。
  • 逗号运算符经常被用在for循环当中:

 

4.11类型转换

  • 在C++语言中,某些类型之间有关联。如果两种类型有关联,那么当程序需要其中一种类型的运算对象时,可以用另一种关联类型的对象或值来替代。换句话说,如果两种类
  • 型可以相互转换(conversion),那么它们就是关联的。举个例子,考虑下面这条表达式,它的目的是将ival初始化为6:
  • int ival = 3.541 + 3; / / 编译器可能会警告该运算损失了精度
  • 加法的两个运算对象类型不同:3.541的类型是double,3的类型是int。C++语言不会直接将两个不同类型的值相加,而是先根据类型转换规则设法将运算对象的类型统一后再求值。上述的类型转换是自动执行的,无须程序员的介入,有时甚至不需要程序员了解。因此,它们被称作隐式转换(implicitconversion)。算术类型之间的隐式转换被设计得尽可能避免损失精度。很多时候,如果表达式中既有整数类型的运算对象也有浮点数类型的运算对象,整型会转换成浮点型。在上面的例子中,3转换成double类型,然后执行浮点数加法,所得结果的类型是double。接下来就要完成初始化的任务了。在初始化过程中,因为被初始化的对象的类型无法改变,所以初始值被转换成该对象的类型。仍以这个例子说明,加法运算得到的double类型的结果转换成int类型的值,这个值被用来初始化ival。由double向int转换时忽略掉了小数部分,上面的表达式中,数值6被赋给了ival。

何时发生隐式类型转换

  • 在下面这些情况下,编译器会自动地转换运算对象的类型:
  • 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
  • 在条件中,非布尔值转换成布尔类型。
  • 初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。
  • 如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
  • 如第6章将要介绍的,函数调用时也会发生类型转换。

4.11.1算术转换

  • 算术转换(arithmeticconversion)的含义是把一种算术类型转换成另外一种算术类型,这一点在2.1.2节(第32页)中已有介绍。算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。例如,如果一个运算对象的类型是long double,那么不论另外一个运算对象的类型是什么都会转换成long double。还有一种更普遍的情况,当表达式中既有浮点类型也有整数类型时,整数值将转换成相应的浮点类型。

整型提升

  • 整型提升 ,负责把小整数类型转换成较大的整数类型。对于bool、char、signedchar、unsignedchar、short和unsignedshort等类型来说,只要它们所有可能的值都能存在int里,它们就会提升成int类型;否则,提升成unsigned int类型。就如我们所熟知的,布尔值false提升成0、true提升成1。较大的char类型(wchar_t、charl6_t、char32_t)提升成int、unsignedint、long、unsigned long、long long和unsigned long long中最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。

无符号类型的运算对象

  • 如果某个运算符的运算对象类型不一致,这些运算对象将转换成同一种类型。但是如果某个运算对象的类型是无符号类型,那么转换的结果就要依赖于机器中各个整数类型的相对大小了。像往常一样,首先执行整型提升。如果结果的类型匹配,无须进行进一步的转换。如果两个(提升后的)运算对象的类型要么都是带符号的、要么都是无符号的,则小类型的运算对象转换成较大的类型。
  • 如果一个运算对象是无符号类型、另外一个运算对象是带符号类型,而且其中的无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的。例如,假设两个类型分别是unsigned int和int,则int类型的运算对象转换成unsigned int类型。需要注意的是,如果int型的值恰好为负值,其结果将以2.1.2节(第32页)介绍的方法转换,并带来该节描述的所有副作用。
  • 剩下的一种情况是带符号类型大于无符号类型,此时转换的结果依赖于机器。如果无符号类型的所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型。如果不能,那么带符号类型的运算对象转换成无符号类型。例如,如果两个运算对象的类型分别是long和unsigned int,并且int和long的大小相同,则long类型的运算对象转换成unsignedint类型;如果long类型占用的空间比int更多,则unsigned int类型的运算对象转换成long类型。

理解算术转换

  • 要想理解算术转换,办法之一就是研究大量的例子:

  • 在第一个加法运算中,小写字母,a,是char型的字符常量,它其实能表示一个数字值(参见2.1.1节,第30页)。到底这个数字值是多少完全依赖于机器上的字符集,在我们的环境中,a,对应的数字值是97。当把,a,和一个longdouble类型的数相加时,char类型的值首先提升成int类型,然后int类型的值再转换成longdouble类型。最终我们把这个转换后的值与那个字面值相加。最后的两个含有无符号类型值的表达式也比较有趣,它们的结果依赖于机器

4.11.2其他隐式类型转换

  • 除了算术转换之外还有几种隐式类型转换,包括如下几种。
  • 数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:

  • 当数组被用作decltype关键字的参数,或者作为取地址符(&)、sizeof及typeid(第19.2.2节,732页将介绍)等运算符的运算对象时,上述转换不会发生。同样的,如果用一个引用来初始化数组(参见3.5.1节,第102页),上述转换也不会发生。我们将在6.7节(第221页)看到,当在表达式中使用函数类型时会发生类似的指针转换。
  • 指针的转换:C++还规定了几种其他的指针转换方式,包括常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*.15.2.2节(第530页)将要介绍,在有继承关系的型间还有另外一种指针转换的方式。

4.11.3显式转换

  • 有时我们希望显式地将对象强制转换成另外一种类型。例如,如果想在下面的代码中执行浮点数除法:
  • int i,j;   double   slope  = i/j;
  • 就要使用某种方法将i和/或j显式地转换成double,这种方法称作强制类型转换(cast)
  • 然有时不得不使用强制类型转换,但这种方法本质上是非常危险的

命名的强制类型转换

  • 一个命名的强制类型转换具有如下形式:
  • cast-name<type> (expression); 其中,type是转换的目标类型而expression是要转换的值。如果是引用类型 ,则结果是左值 。 cast-name是static cast、dynamic cast、const cast和
    reinterpret_cast中的一种。dynamic_cast支持运行时类型识别,我们将在19.2节(第730页)其做更详细的介绍。cast-name指定了执行的是哪种转换。

reinterpret__cast

  • reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。举个例子,假设有如下的转换

  • reinterpret_cast本质上依赖于机器。要想安全地使用reinterpret_cast必须对涉及的类型和编译器实现转换的过程都非常了解;

 4 . 1 2 运算符优先级表

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git 之 git tag标签使用

目录一、简介二、本地tag操作1、创建tag标签&#xff08;1&#xff09;创建轻量标签&#xff08;2&#xff09;创建附注标签2、查看tag标签&#xff08;1&#xff09;查看标签列表&#xff08;2&#xff09;查看标签提交信息&#xff08;3&#xff09;在提交历史中查看标签3、删…

C++primer 第 5 章语句 5.2语句作用域 5.3条件语句 5 . 4 迭代语句 5.5跳转语句 5.6 try语句块和异常处理

5 . 1 简单语句 C语言中的大多数语句都以分号结束&#xff0c;一个表达式&#xff0c;比如ival 5 , 末尾加上分号就变成了表达式语句(expression statement)。表达式语句的作用是执行表达式并丢弃掉求值结果&#xff1a;ival 5&#xff1b; // 一条没什么实际用处的表达式语…

英语口语-文章朗读Week9Thursday

英语文章 Everyone has his or her own dreams. Some people wants to be millionaires so they can give many generous donations later; some people want to be scientists so they can bring many conveniences to the world; some people only want to be bus-drivers s…

操作系统 内存管理相关知识

cpu执行程序的基本过程 译码器 输入为n管脚&#xff0c;输出为2^n根管脚&#xff0c;编号为从0到2^(n-1)&#xff0c;用少的输入端控制更多的输出端最常用的是三八译码器AD(Address bus)地址总线: 选中一行数据每一行 8bit 组成8吧B cpu输入端32根线&#xff0c;输出端就可以控…

Chrome浏览器必装插件!尤其程序猿!

Chrome 浏览器有一个好处&#xff0c;就是插件极其丰富&#xff0c;只有你想不到的&#xff0c;没有你找不到的&#xff0c;这恐怕是 Chrome 浏览器被众多爱好者钟爱的原因吧。 言归正传&#xff0c;今天来给大家推荐 10 款我自己珍藏的 Chrome 浏览器插件。 1、crxMouse Ch…

英语口语-文章朗读Week10 Monday

英语文章 Here are some valuable suggestions which may assist you in landing good job First, make your resume clear and associate it with the position you are applying for. Try to add details like your temporary jobs at college or your former jobs Second, …

英语口语-文章朗读Week10 Wednesday

英语文章 Everyone needs sleep for survival, but how much? It is said that eight hours of sleep is fundamental to a healthy person. But today, many people are sacrificing their sleep time。 Modern people have so many alternatives: cell phones, PCs, TVs, g…

嵌入式Linux多任务编程 进程 管道 命名管道

进程 进程是一个可并发执行的具有独立功能的程序关于某个数据集合的一次执行过程&#xff0c;也是操作系统执行资源分配和保护的基本单位。程序的一次执行就是一个进程一个程序可以派生多个进程多个不同程序运行的时候&#xff0c;也会有多个相对应的进程与其相互对应进程是动…

英语口语-文章朗读Week10 Thursday

英语文章 There are many customs and traditions in Chinese civilization. Here, we will talk about the development of the way people greet each other: In ancient times, people had to kneel to those who were superior to them. This custom remained until the …

Linux进程之间通信 信号

2) SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出&#xff0c;用于通知前台进程组终止进程。 3) SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。 15)…

Linux进程之间通信 消息队列

使用命令 ipcs -q 查看对应的消息队列代码 文件接收者 #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <string> #include <signal.h> #include <wait.h> #include <sys/msg.h> #include <cstring&g…

c++面向对象高级编程 学习二 带指针的类

带指针的类&#xff0c;必须要自己写拷贝构造和赋值构造 拷贝构造&#xff1a;参数和类的类型一样的构造函数 赋值构造&#xff1a;重写操作符&#xff0c;且其参数和类的类型一样 class String { public: String(const char* cstr 0); String(const String& str); Strin…

英语口语 week11 Tuesday

英语文章 It was a cold and gloomy winter afternoon, people with their chilled hands tucked into their pockets or hidden in their sleeves. Fred was in a depressed mood, just like the weather,for he failed to get any award in the debate competition When he …

进程之间通信 共享内存

命令 ipcs 命令查看共享内存、消息队列、管道等相关信息ipcs -m 查看共享内存的信息代码 创建共享内存共享内存 关联 进程分离共享内存删除共享内存 #include <sys/shm.h> #include <iostream>#define BUF_SIZE 1024int main() {int share_id 0;//创建共享内存i…

c++面向对象高级编程 学习三 堆、栈和内存泄漏

栈&#xff0c;是存在于某作用域的一块内存空间。在函数体内声明的任何变量&#xff0c;其所使用的内存空间均来自于栈。 堆&#xff0c;是指由操作系统提供的一块global内存空间&#xff0c;程序可动态分配获得若干内存空间块。 new操作符生成的对象所占用的内存空间即是从堆中…

clion编写C++ 使用多线程时候,CMakeLists.txt书写,引用-pthread

添加如下一行 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 具体的例子 cmake_minimum_required(VERSION 3.17) project(mutex_learn)set(CMAKE_CXX_STANDARD 14)set(BOOST_ROOT "/usr/local/include/boost") #添加头文件搜索路径 include_direc…

c++面向对象高级编程 学习四 静态、类模板、函数模板

静态static&#xff1a;静态数据和静态函数&#xff0c;在内存中只有一份&#xff0c;不会随着创建对象的数目的增加而增加 static数据&#xff1a;比如银行的account类中&#xff0c;账户名是普通数据&#xff0c;100个对象会有100个账户名&#xff0c;但利率都是相同的&#…

线程的编程

完整代码 #include <sys/shm.h> #include <iostream> #include <unistd.h> #include <pthread.h>void * child1(void *arg){pthread_t tid pthread_self();printf("1 thread %lu \n",tid);}int main(int argc,char* argv[]) {int result{…

英语口语 week11 Friday

英语文章 I very much like simplicity in life. For me, college is far more than a place to improve my intellectual abilities Every weekend, I usually have a walk along the way to the front gate of Mount Qingcheng, enjoying the intense aromas of flowers on …