一:前提知识


1 )运算符:

运算符包含了运算法则、优先级和结合性;

运算符优先级:

单目 > 算术 > (左移、右移) > 关系 > (按位与、异或、或) > 逻辑 > 条件运算符 > 赋值 > 逗号 ;

2 )表达式:

由常量,变量以及运算符组成的式子;

二:基本知识


1 )算术运算符:+ - * / %

#include<iostream>
using namespace std;int main() {int a = 10, b = 20;cout << a + b << endl; // 30cout << a - b << endl; // -10cout << a * b << endl; // 200cout << a / b << endl; // 0cout << a % b << endl; // 10return 0;
}

%求模(取余):两边必须是 整数类型 ;
求模之后的符号问题:如下代码表示取余的结果等于被取余数的符号;

#include<iostream>
using namespace std;int main() {int a = 10, b = 20;cout << a % b << endl;  // 10a = -10; b = 20;cout << a % b << endl;  // -10a = -10; b = -20;cout << a % b << endl;  // -10a = 10; b = -20;cout << a % b << endl;  // 10return 0;
}

2 )自增自减:自己(变量)本身(+1)或者(-1)

前置:++a,,–a 先自加(减),再运算;
后置:a++,a-- 先运算,再自加(减);
口诀:运算前,数前置;运算后,数后置;

#include<iostream>
using namespace std;int main() {int a = 1, b = 2;b = a++ + a;cout << "b= " << b << " , a= " << a << endl;  // 2 2b = a++ + a++ + (++a);cout << "b= " << b << " , a= " << a << endl;  // 9 5return 0;
}

前置和后置的区别:

  1. 优先级:后置 大于 前置;
  2. 效率:前置 大于 后置;

原因:

  1. ++a表示取a的地址,增加它的内容,然后把值放在寄存器中;
  2. a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;

代码如下:
原文:点击

#include<iostream>
using namespace std;
/*++a表示取a的地址,增加它的内容,然后把值放在寄存器中;a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;
*/
class Age {public:Age& operator++() { //前置++++i;return *this;}const Age operator++(int) { //后置++Age tmp = *this;++(*this);  //利用前置++   return tmp;}Age& operator=(int i) { //赋值操作this->i = i;return *this;}
private:int i;
};
int main() {return 0;
}

从上述代码,我们可以看出前置++和后置++,有4点不同:

  1. 返回类型不同
  2. 形参不同
  3. 效率不同

1. 返回类型不同:

前置的返回类型是 Age& ;后置的返回类型是 const Age ;
这就意味着,前置返回的是左值,后置返回的是右值;
左值和右值,决定了 前置 和 后置 的性质和用法的不同;

int main() {Age age;(age++)++;  //编译错误++(age++);  //编译错误age++ = 1;   //编译错误(++age)++;  //OK++(++age);  //OK++age = 1;   //OKreturn 0;
}

后置的类型是 const Age ,自然不能对它进行前置++、后置++、赋值等操作;
前置的类型是 Age& ,当然可以对它进行前置++、后置++、赋值等操作;

a++的返回类型为什么要是const对象呢?
有两个原因:

  1. 如果不是const对象,a(++)++这样的表达式就可以通过编译。但是,其效果却违反了我们的直觉 。a其实只增加了1,因为第二次自增作用在一个临时对象上;
  2. 另外,对于内置类型,(i++)++这样的表达式是不能通过编译的。自定义类型的操作符重载,应该与内置类型保持行为一致 ;

a++的返回类型如果改成非const对象,肯定能通过编译,但是我们最好不要这样做;

++a的返回类型为什么是引用呢?

这样做的原因应该就是:与内置类型的行为保持一致。前置++返回的总是被自增的对象本身。因此,++(++a)的效果就是a被自增两次;

2. 形参不同:

前置没有形参,而后置有一个int形参,但是该形参也没有被用到;
很奇怪,难道有什么特殊的用意?
其实也没有特殊的用意,只是为了绕过语法的限制;

前置与后置的操作符重载函数,函数原型必须不同。否则就违反了“重载函数必须拥有不同的函数原型”的语法规定;
虽然前置与后置的返回类型不同,但是返回类型不属于函数原型。为了绕过语法限制,只好给后置增加了一个int形参;
原因就是这么简单,真的没其他特殊用意。其实,给前置增加形参也可以;增加一个double形参而不是int形参,也可以。只是,当时就这么决定了;

3. 效率不同:

如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作;
前置++的效率更高,理由是:后置++会生成临时对象;
从Age的后置++的代码实现也可以看出这一点;

const Age operator++(int) { //后置++Age tmp = *this;   ++(*this);  //利用前置++   return tmp;
}

很明显,tmp是一个临时对象,会造成一次构造函数和一次析构函数的额外开销。虽然,编译器在某些情况下可以优化掉这些开销。但是,我们最好不要依赖编译器的行为;
所以,在非内置类型的时候,尽量使用 前置 ,因为效率高(后置自增,效率低);

3 )赋值运算符:= += -= *= … 左边必须是变量

  1. 结合性:从右往左;
  2. 右边必须看成一个整体;
b *= a + b; // 等价于b=b*(a+b)

4 )关系运算符:> < >= <= == != 运算结果为bool型

5 )逻辑运算符:!逻辑非 , &&逻辑与 , ||逻辑或 , 运算结果为bool型

逻辑运算符 && 和 || 的优先级:

优先级:&& 大于 || ;

  1. 逻辑非;

真则为假,假则为真;

  1. 逻辑与:

同真为真,否则为假。先算前面的式子,前面式子为假,后面的式子不参与运算;

  1. 逻辑或;

有真为真,否则为假。先算前面的式子,前面式子为真,后面式子不参与运算;
公式:

6 )条件运算符:? :

a ? b : c; // 如果a为真,结果为b,否则为c;

a ? b : c; // 如果a为真,结果为b,否则为c;

7 )位运算符:以2进制补码形式参与运算。

在内存中数据是以二进制补码形式保存的;

  1. 原码:最高位表示符号位,其他位表示大小;
  2. 反码:正数反码与原码相同;负数:符号位不变,按位取反(0->1,1->0);
  3. 补码:正数补码与原码相同;负数:反码+1;

口诀:补码的补码就是原码。

位运算符 ~ , << , >> , & , ^ , |

  1. ( ~ )按位非:按位取反( 0 -> 1,1 -> 0 );

公式:~n=-(n+1);

/*
5的原码:0 0000000 00000000 00000000 00000101
5的反码:0 0000000 00000000 00000000 00000101
5的补码:0 0000000 00000000 00000000 00000101
~       1 1111111 11111111 11111111 11111010 补码(看成原码求补码)1 0000000 00000000 00000000 00000101 反码1 0000000 00000000 00000000 00000110 补码(补码的补码就是原码)-6~n=-(n+1)
*/
cout << ~5 << endl;//-6
  1. ( << )左移:低位补0;

公式:n << m 等价于 n*2 的 m 次方;

char c = 10;
/*10的原码:  0 0000000 00000000 00000000 0000101010的反码 : 0 0000000 00000000 00000000 0000101010的补码 : 0 0000000 00000000 00000000 00001010<<5       0 0000000 00000000 00000001 01000000 补码(看成原码求补码)0 0000000 00000000 00000001 01000000 反码0 0000000 00000000 00000001 01000000  补码(补码的补码就是原码)320n << m 等价于 n*2 的 m 次方
*/
cout << (c << 5) << endl; // 320
c = c << 5; // c=320  数据溢出 320-256=64
cout << (int)c << endl; // 64
c = 10;
c = c << 4; // c=160 数据溢出 160-256=-96
cout << (int)c << endl; // -96
  1. ( >> )右移:正数,高位补 0 ,负数,高位补 1 ;

公式:

n >> m;
n为正数: n 除以 2 的 m 次方
n为负数:

1.能整除 n除以2的m次方
2.不能整除 n除以2的m次方-1

/*n >> m;n为正数: n 除以 2 的 m 次方n为负数:1.能整除 n除以2的m次方2.不能整除 n除以2的m次方-1
*/
cout << (10 >> 2) << endl; // 等价于10/4=2
  1. ( & )按位与:同 1 为 1 ,否则为 0

公式:同 1 为 1 ,否则为 0

/*
5的补码: 00000000 00000000 00000000 00000101
7的补码: 00000000 00000000 00000000 00000111&  0 0000000 00000000 00000000 00000101 补码(看成原码求补码)0 0000000 00000000 00000000 000001010 0000000 00000000 00000000 00000101 补码(补码的补码就是原码)5
*/
cout << (5 & 7) << endl; // 5
  1. ( ^ )按位异或:同则为 0 ,异则为 1

公式:同则为 0 ,异则为 1

/*
5的补码: 00000000 00000000 00000000 00000101
7的补码: 00000000 00000000 00000000 00000111^   0 0000000 00000000 00000000 00000010 补码(看成原码求补码)0 0000000 00000000 00000000 00000010 0 0000000 00000000 00000000 00000010 补码(补码的补码就是原码)2
*/
cout << (5 ^ 7) << endl; // 2
  1. ( | )按位或:同 0 为 0 ,否则为 1

公式:同 0 为 0 ,否则为 1

/*
5的补码: 00000000 00000000 00000000 00000101
7的补码: 00000000 00000000 00000000 00000111|   0 0000000 00000000 00000000 00000111 补码(看成原码求补码)0 0000000 00000000 00000000 000001110 0000000 00000000 00000000 00000111 补码(补码的补码就是原码)7
*/
cout << (5 | 7) << endl; // 7

8 )逗号运算符:优先级最低

  1. 由逗号运算符组成的式子,称为逗号表达式;
  2. 从左往右依次计算,最后一个式子就是整个表达式的值;
#include<iostream>
using namespace std;int main() {int a = 1, b = 2, c = 0;c = a, b; // 等价于: c = a;cout << "c = " << c << endl;return 0;
}

9 )整型提升

整型提升:bool、char、unsigned char、short、unsigned short、int、unsigned int运算时,自动提升为int或者unsigned int;

10 )运算结果的精度问题

运算结果的类型以参与运算中类型精度高的为准;
浮点数精度 大于 整型精度;

11 )交换两个整型变量的值

经过第三方变量;

#include<iostream>
using namespace std;
int main() {int a = 10, b = 20;int temp = a;a = b;b = temp;cout << "a = " << a << " , b = " << b << endl;return 0;
} // 浪费了空间

缺点浪费了空间;

不经过大三方变量;
方法一;

#include<iostream>
using namespace std;int main() {int a = 10, b = 20;a = a + b; // a + b 数据可能溢出b = a - b;a = a - b;cout << "a = " << a << " , b = " << b << endl;return 0;
} // 缺点容易数据溢出

缺点容易数据溢出;

方法二;

#include<iostream>
using namespace std;int main() {int a = 10, b = 20;a = a ^ b;b = a ^ b;a = a ^ b;cout << "a = " << a << " , b = " << b << endl;return 0;
}

通过位运算( ^ :按位异或,同则为 0 ,否则为 1 )

12 ) 判断一个整数 n 是正数还是负数

int n = 20;
n >> 31 ? "负数" : "正数";

13 )判断一个正整数 x 是否为 2 的 n 次幂

int x = 20;
!(x & x - 1) ? "是" : "不是";

14 )判断一个数的奇偶性

int x = 20;
x & 1 ? "奇数" : "偶数";

C++ 的算术、自增自减、位运算、关系与逻辑运算符(数据的操作)相关推荐

  1. 【位运算】代码中的常用操作

    作者:力扣(LeetCode) 链接:https://www.zhihu.com/question/38206659/answer/736472332 来源:知乎 著作权归作者所有.商业转载请联系作者 ...

  2. C++ 算法篇 位运算

    学习目标 1. 理解与掌握 C++ 中的位运算.  2. 灵活应用位运算优化程序. 任何信息在计算机中都是采用二进制表示的,数据在计算机中是以补码形式存储的,位运算就是直接对整数在内存中的二进制位进行 ...

  3. 位运算简介及实用技巧(一):基础篇

    去年年底写的关于位运算的日志是这个Blog里少数大受欢迎的文章之一,很多人都希望我能不断完善那篇文章.后来我看到了不少其它的资料,学习到了更多关于位运算的知识,有了重新整理位运算技巧的想法.从今天起我 ...

  4. 位运算(转自matrix67)

    http://www.matrix67.com/blog/archives/263 (原文链接) 什么是位运算? 程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算说穿了,就是直接对整数在内 ...

  5. 位运算 之(1) 按位与(AND) 操作【转载】

    由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a.b中对应位同时为1,则对应结果位也为 ...

  6. 位运算 之(1) 按位与(AND) 操作

    文章来源:ktyanny 位运算操作 由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a ...

  7. Java02-day02【运算符(赋值、关系、逻辑、三元、算数、自增自减)、分支语句(顺序结构、if语句)】

    java零基础入门到精通(2019版)[黑马程序员] 视频+资料:[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] &qu ...

  8. c语言:自增自减运算符的操作详解

    博主在回忆c语言的基本知识时,突然发现自增自减运算符(--.++)这个知识点有些模糊不清,故博主为了给同为小白的同学们提供一些经验,特写下这篇文章. 首先,自增自减运算符共有两种操作方式. 比如,我先 ...

  9. C++位运算详解(转)

    位运算是对表示数据的基本单元进行"加和","减除"的方法. 首先一个位(bit)单位就是0或1,硬件表示就是一个肪冲的开和,这是硬软通迅最基本的单元.我们所说的 ...

  10. 位运算以及位运算的应用

    位运算 什么是位运算 在计算机系统中,所有数据都是以二进制的形式进行存储,位运算就是对二进制中进行操作.所有的计算都是通过位运算进行实现的. 整数在计算机中 原码 原码是最简单的表示法,对于一个整数而 ...

最新文章

  1. undefined symbol: _Py_ZeroStruct
  2. ML之Clustering之H-clustering:Hierarchical clustering算法相关论文、主要思路、关键步骤、代码实现等相关配图之详细攻略
  3. java增加内容辅助_Eclipse代码自动提示(内容辅助content assist)
  4. arcgis-online-python-scripts
  5. Java基础面试16问
  6. 微软自带的防反编译工具dotfuscator.exe的使用
  7. 3. 线性表的链式结构
  8. ubuntu 安装 virt-manager 虚拟机
  9. sql多表查询的总结
  10. Ubuntu 12.04.2搭建nfs服务器
  11. denoiser插件_最新版AE/PR红巨人调色降噪插件合集 Magic Bullet Suite 13.0.9 Win/Mac
  12. Java代码实现时钟
  13. 手机扫码枪app,手机扫码,内容上电脑表格
  14. 绘画教程:如何轻松画出皮肤的挤压感
  15. 无理数的无理数次幂的结果可以是有理数?(以〖√2〗^√2为例)
  16. 【动漫风格迁移】基于AnimeGAN的安卓APP工具
  17. Two Teams Composing
  18. 通达信l2数据接口与MACD指标结合
  19. 【星辰傀儡线·命运环·卷二 尘埃】 4 蓝月
  20. 【180927】坦克大战游戏源码

热门文章

  1. 1599 元至 1999 元 ,Redmi Note 9 Pro 正式发布:骁龙 750G + 1 亿像素相机
  2. PCF8591 +MQ3 +LCD1602+89C51RC(酒精传感器)
  3. ASA广告投放策略:建立、更新你的关键词库
  4. 如何开展兼容性测试?兼容性测试有什么作用?
  5. MAKEFILE【4】-Makefile中的wildcard用法
  6. 编译《视觉SLAM十四讲》ch5里joinmap出现 ***/anaconda3/lib/libpng16.so.16:‘inflateValidate@ZLIB_1.2.9’未定义的引用
  7. 数独问题之排除法和唯余法
  8. 安防摄像头无法接入国标GB28181视频平台EasyGBS问题排查与解决方案
  9. 国赛学习——5种数学规划模型
  10. android.mk 条件编译,android openSSL 的苦逼历程