1. 前言

编写程序时,数据确定后,就需要为数据提供相应的处理逻辑(方案或算法)。所谓逻辑有 2 种存在形态:

  • 抽象形态:存在于意识形态,强调思考过程,与具体的编程语言无关。
  • 具体形态:通过代码来实现。需要使用表达式描述完整的计算过程。

表达式2 个部分组成:

  • 数据。也可称为操作数。

  • 运算符

    运算符是计算机语言提供的能对数据进行基本运算操作的功能体。开发者在实现自己的逻辑运算时,需要组合这些运算符来描述自己的逻辑运算过程。

Tip: 可以把C++运算符看成一种特殊语法格式的函数,或把C++中的函数当成一种特殊的运算符。

在使用运算符时,需要遵守下面的 2个基本原则:

  • 运算符对操作的数据有内置的类型要求。如数学运算符要求操作数是数字类型。
  • 如果运算符需要多个操作数时,则要求数据类型必须相同。如果出现类型不一致时,编译器会试着把不同类型的数据转换成同类型的数据后再进行运算。开发者也可以显示进行强制类型转换。

2. 运算符种类

C++中的运算符非常多,如下是几类常用的运算符:

  • 算术运算符。
  • 逻辑、关系运算符。
  • 赋值运算符。
  • 递增、递减运算符。
  • 成员访问运算符。
  • 条件运算符。
  • 位运算符。
  • sizeof 运算符。
  • 逗号运算符。

使用运算符前,需要理解如下几个概念:

  • 运算符的优先级: 不同类别中的运算符的优先级是不相同的。当在一个表达式中出现多个运算符时,则需要根据运算符的优先级进行先后运算。

  • 运算符的操作数: 作用于一个操作数的运算符为一元运算符,作用于两个操作数的运算符为二元运算符C++中还有一个可作用于三个操作数的条件运算符

  • 结合性: 当复杂表达式中的多个运算符的优先级相同时,则要根据运算符的结合性进行运算。如 100/4*8这个表达式,/*的优先级是相同,因乘、除都是具有从左到右的结合性。所以先计算100/4=25再计算25*8

    Tip: 只有当两个运算符作用于同一个操作数时,优先级和结合性才有意义。

C++中的基础运算符较多,且因C++是弱类型语言,每一种运算符在使用过程中都存在很多细节问题。算术运算符又是运算符中的基础运算符。

本文试图通过讲解清楚算术运算符,让阅读者了解使用C++运算符时应该注意的事项。

3. 算术运算符

3.1 功能描述

算术运算符用来对数字型数据进行数学语义上的。此类中有 5个运算符:

  • +:对 2数字类型的数据进行数学语义上的加法运算。
  • -:对 2数字类型的数据进行数学语义上的减法运算。
  • *:对 2数字类型的数据进行数学语义上的乘法运算。
  • /:对 2数字类型的数据进行数学语义上的除法运算。
  • %:取或取操作运算符。运算结果是两个操作数相除后的余数部分,不能用于浮点数据类型。

算术运算符是二元运算符。使用时,需要提供 2 个操作数。

3.2 运算符重载问题

C++可以重载运算符,所谓重载运算符,指同一个运算符可以根据使用时的上下文信息,表现出不同的运算能力。如-运算符, 当作为二元运算符时,用来对操作数进行相减操作。

int num1=30;
int num2=20;
//此处的 - 运算符表现出减法运算能力
int res=num1-num2;
cout<<res<<endl;
//输出结果: 10

当作为一元运算符时,则是取的意思。如下代码:

int num=-10;
int num01=-num;
cout<<num01<<endl;
//输出结果为 10,负负为正

同理,+运算符也存在重载。

运算符重载是C++中的一个特色。

对于有符号数据类型而言,如果在字面常量前面没有显示提供正、负符号,则默认为 +(正)符号。

3.3 两数相除的问题

/运算符作用于 2 个整型数字时,会得到舍弃小数点后的整数部分数值,或称为两数相除的,意味着会丢失精度。

如下代码:

int num1=7;
int num2=3;
int res=num1/num2;
cout<<res<<endl;
//输出结果:2,丢失精度

如果要保留两个数字相除的精度,则应该以浮点数据类型的身份进行相除。

double num1=7;
double num2=3;
double res=num1/num2;
cout<<res<<endl;
//输出结果:2.33333

%运算符作用于 2 个整型类型的数据时,运算结果是 2 个数字相除之后的余数部分。如下代码:

int num1=5;
int num2=3;
int res=num1 % num2;
cout<<res<<endl;
//输出结果:2 。

%用于浮点数据类型相除时,会出现编译错误。也就是 %只能用于整型数据的运算,不能用于浮点数据类型。

3.4 关 于/%运算符的问题

  • 2 个操作数据都是数时。
int num1=21;
int num2=8;
int res=num1 / num2;
cout<<" / 运算:"<<res<<endl;
res=num1 % num2;
cout<<" % 运算:"<<res<<endl;

/%动算符的输出结果都是数。

/ 运算:2
% 运算:5
  • 2 个操作数都为数时。
int num1=-21;
int num2=-8;
int res=num1 / num2;
cout<<" / 运算:"<<res<<endl;
res=num1 % num2;
cout<<" % 运算:"<<res<<endl;

输出结果,一个是正数,一个是负数。

 / 运算:2
% 运算:-5
  • 2 个操作数中被除数为负,除数为正时。
int num1=-21;
int num2=8;
int res=num1 / num2;
cout<<" / 运算:"<<res<<endl;
res=num1 % num2;
cout<<" % 运算:"<<res<<endl;

输出结果都是负数。

/ 运算:-2
% 运算:-5
  • 2 个操作数中被除数为正,除数为负时。
int num1=21;
int num2=-8;
int res=num1 / num2;
cout<<" / 运算:"<<res<<endl;
res=num1 % num2;
cout<<" % 运算:"<<res<<endl;

输出结果为一负一正。

/ 运算:-2
% 运算:5

结论

  • 2 个数字使用 %运算符进行相除操作时,运算结果的正负号与 num1操作数(被除数)的正负号保持一致。
  • /运算符运算结果的正负号和数学上的语义一致。两个操作数都为正或为负时则正正得正负负得正。两个操作数为一正一负时:则正负得负

3.5 数据溢出问题

在使用算术运算符时,有可能出现数据溢出现象。如下代码:

short num=32767;
short num01=num+1;
cout<<num01<<endl;

输出结果:

数字:-32768

无符号short(16位)的类型数据的最大值是 32767,在此数字上加一,num01的值理论是上 32768。但实际结果是 -32768。因为 32768已经超过short范围,编译器会重新计算出一个新的结果(并不是预期值)。这种现象叫数据溢出

对于无符号 short,可以认为其有 2 部分,一部分为负数,一部分为正数。当正数溢出后,会进入负数部分。

如下代码,因溢出,超过了负数区域最小值,会溢出到正数区域。

short num1=-32768;
short num2=num1-1;
cout<<num2;
//输出结果:32767

数据溢出发生在当把数据类型范围大的数据存储到数据类型小的类型变量中时。

  • double 数据存储到 int 类型变量中。
  • int 类型的数据存储到 short类型变量中。
  • long long int 类型的数据存储到 int 类型变量中时。
  • ……

数学运算符也可以用于指针类型运算,因指针变量其数据本质就是数字数据。但指针变量不能用于乘法和除法,加、减的语义是指针的向前后后移动,乘法、除法没有语义价值。

3.6 类型转换

根据运算符的基本使用原则,要求所有操作数的类型必须相同。

有时,在一个表达式中,即使存在多个操作数的类型不一致,也能正常工作。那是因为,编译器会把不同的数据类型转换成一致,然后再进行运算。

由编译器完成的类型转换,称为自动(隐式)类型转换:

  • 整型提升C++boolcharunsigned charsigned charshort值转换为 int。这些转换被称为整型提升。
  • 浮点提升:整型类型自动向浮点类型转换,如 intdouble转换。这种转换是不会存在数据丢失问题,但会产生空间浪费。
  • 向下缩窄: 当目标类型小于原类型时,如doubleint转换,int类型向short转换时,这种转换是可以的,但会发生数据丢失的情况。可能会得不到预期结果。

碗里的水倒到缸里,不会丢失水。

缸里面的水倒到碗里,如果缸里面的水很少,不够或者刚够一碗水,不会发生水丢失。但是,这里会有潜在丢失问题,因为生活常识告诉我们,缸里面的水往往是要超过一个碗所能盛下的容量。

所以,向下缩窄存在潜在的数据丢失风险。

如下代码,其中发生了 2 次自动类型转换,有数据丢失的潜在风险。

double num1=7;
int num2=3;
int res=num1/num2;
cout<<res<<endl;
//输出结果: 2
  • 浮点提升num2中的数据会被转换成double数据类型,让右边的表达式符合同类型原则。此时,右边表达式运算后的结果类型为 double。这一步不会发生数据丢失问题。
  • 向下缩窄: 左边的res变量类型为int ,编译器会把右边的double类型结果转换成 int。如果数值大于int类型范围时,则会出现丢失精度问题。

如下代码,则不会发生数据丢失问题:

double num1=7;
int num2=3;
double res=num1/num2;
cout<<res<<endl;
//输出结果:2.33333

如下的代码,也会发生自动类型转换。

int num1=20;
char num2='A';
int res=num1+num2;
cout<<res<<endl;
//输出结果: 85
  • char类型会转换成 int类型。
  • 字符保存在计算机上时,需要对其进行数字编码,字符转换成 int的数字是底层的编码数字。

如下代码,也会发生自动类型。

int num1=20;
bool num2=true;
int res=num1+num2;
cout<<res<<endl;
  • C++中,bool数据类型本质上就是int类型。
  • true会转换为 1false会转换为0

3.7 {}赋值语法

C++在进行自动类型转换时,如果目标类型小于原类型时,也是能够转换的,这种现象叫缩窄缩窄会存在潜存数据安全问题。C++11提供了{}赋值语法,会对超过范围的缩窄进行编译提示。如下代码。

  • 44555 数字已经超过 char 范围,向下缩窄不被允许。
char c1= {44555};
  • X是一个变量,在运行时,x有可能被修改,并让其值大于 char数字范围,向下缩窄不被允许。
int x=66;
char c4={x};

3.8 强制类型转换

C++允许开发者显式地进行类型转换。语法格式有 2 种:

  • (目标类型名)变量。
  • 目标类型名(变量)。

强制类型转换不会修改变量本身,而是创建一个新的值。用于表达式中进行计算。

double num1=23.6;
//C++强制类型转换语法
int num2=double(num1);
cout<<num2<<endl;
//C 强制类型转换语法
num2=(double)num1;
cout<<num2<<endl;

C++还提供了 4 个类型转换运算符,使得转换过程更规范。这里只做简要介绍,有兴趣者可以深入了解一下。

  • dynamic_cast。在类层次结构中进行向上转换。
  • const_cast。用于执行只有一种用途的类型转换,即改变值为 constvolatile
  • static_cast。只有当类型之间可以隐式转换时才能转换。
  • reinterpret_cast。用于一些有很大潜在危险的类型转换。

3.9 auto 语法

auto关键字在C++的作用是自动类型推导。在声明变量时,可以使用 auto关键字,不指定变量的类型说明。编译器会根据变量中所存储的数据的类型自动推导出数据类型。

// num 是浮点数据类型
auto num=5.3;
//num1 是整型数据类型
auto num1=4;

PythonJS就是一种动态语言,表现在数据类型可以底层编译器自动识别。

虽然C++auto语法,但C++归属于弱类型语言,在数据类型识别上,一半依赖于开发者的语法约束,一半依赖编译器的自动识别。

4. 总结

C++语言的开放性,数据类型的自我适应性非常灵活。在一个表达式,当出现类型不同的情况时,编译器会试图进行各种类型上的转换,让表达式符合类型相同的运算原则。

宽松的好处是速度快,但也会带来潜在的风险,开发者应该尽可能在语法上对数据类型进行约束,不要过于依赖编译器。养成良好的编码习惯。

C++ 炼气期之算术运算符的更多相关文章

  1. PHP中的运算符---算术运算符、逻辑运算符、赋值运算符、比较运算符

    1.算术运算符 常见的算术运算符 运算类型 运算符 举例 结果 取反运算 - -$a 返回$a的负值 加法运算 + $a + $b 返回$a与$b的和 减法运算 - $a - $b 返回$a与$b的差 ...

  2. java中的算术运算符、赋值运算符、比较运算符、逻辑运算符、条件运算符

    一.算术运算符 算术运算符主要用于进行基本的算术运算,如加法.减法.乘法.除法等. Java 中常用的算术运算符: 其中,++ 和 -- 既可以出现在操作数的左边,也可以出现在右边,但结果是不同滴 例 ...

  3. javascript运算符——算术运算符

    × 目录 [1]一元加 [2]一元减 [3]递增[4]递减[5]加法[6]减法[7]乘法[8]除法[9]求余 前面的话 javascript中的算术操作主要通过算术运算符来实现,本文将介绍算术运算符的 ...

  4. javascript算术运算符详解

    算术运算符 +.-.*./.%.++.-- ++.--分为前缀形式和后缀形式 前缀形式先加减1在执行 后缀形式先执行再加减1 注意 +号用来连接两个字符串 只要+连接的操作数中有一个是字符串型,JS就 ...

  5. 慕课网-安卓工程师初养成-3-2 Java中的算术运算符

    来源:http://www.imooc.com/code/1279 算术运算符主要用于进行基本的算术运算,如加法.减法.乘法.除法等. Java 中常用的算术运算符: 其中,++ 和 -- 既可以出现 ...

  6. JAVA算术运算符、关系运算符和位运算符

    算术运算符 1.java的算数运算符包括+(加).-(减).*(乘)./(除).%(取余),在运算过程中出现的隐式转换原则和C语言一样:2. 高位数据向低位数据转化要使用强制转化: 关系运算符 1.j ...

  7. Java SE ---算术运算符

    算术运算符:(加)+,(减)-,(乘)*,(除)/,(求余)%,自增自减 一,算数运算符:当有若干个变量参与运算时,结果类型取决于这些变量中表示范围最大的那个变量类型.如果参加运算的变量中有整型int ...

  8. c语言学习之基础知识点介绍(四):算术运算符和逗号表达式

    本节主要介绍c语言中运算符. 运算符主要分为四类: 1.算术运算符 加(+),减(-),乘(*),除(/),取余(%,两数相除,得到余数) 2.关系运算符 3.逻辑运算符 4.换位运算符 下面将依次介 ...

  9. Python算术运算符

    Python 运算符 什么是运算符? 本章节主要说明Python的运算符.举个简单的例子 4 +5 = 9 . 例子中,4和5被称为操作数,"+"号为运算符. Python语言支持 ...

随机推荐

  1. Python-初见

    目录 概述 关键字 标准数据类型 Number String List Tuple Set Dictionary 删除对象 数据类型转换 推导式 运算符 迭代器与生成器 迭代器 生成器 函数 参数传递 ...

  2. 高精度减法(C++实现)

    高精度减法 简介 用于计算含有超过一般变量存放不下的非负整数 高精度加法这个过程是模拟的小学竖式减法计算 注:在本文中,我们默认输入的第一个数为被减数,且被减数大于减数 原理基本上与高精度加法相同,仅 ...

  3. Codeforeces 13B

    计算几何二维基础

  4. 关于Vue.cli 脚手架环境中引入Bootstrap时,table表格样式缺失的解决办法

    Vue+bootstrap不能正常使用table的样式 环境:下载官网的本地bootstrap包,然后在vue 的index.html引入bootstrap的css和js环境 问题描述:1. vue里 ...

  5. 图数据库|基于 Nebula Graph 的 BetweennessCentrality 算法

    本文首发于 Nebula Graph Community 公众号 ​在图论中,介数(Betweenness)反应节点在整个网络中的作用和影响力.而本文主要介绍如何基于 Nebula Graph 图数据 ...

  6. SQL注入绕过总结

    花括号绕过 select{x password}from{database.user} union select 1,{x 2},3 特征字符大小写绕过 UniOn SEleCt 1,2,3 MYSQ ...

  7. Hyperledger Fabric 2.2 学习笔记:测试网络test-network

    写在前面 最近被Hyperledger Fabric折磨,归根结底还是因为自己太菜了qwq.学习路漫漫,笔记不能少.下面的步骤均是基于已经成功搭建了Fabric2.2环境,并且拉取fabric-sam ...

  8. 攻防世界-MISC:embarrass

    这是攻防世界高手进阶区的第二题,题目如下: 点击下载附件一,得到一个压缩包,解压后得到一个流量包 用wireshake打开,CTRL+F搜索字符串flag,记住要选择分组字节流 点击查找 在点击几次过 ...

  9. Jenkins Build step 'Execute shell' marked build as failure

    问题出现: Jenkins一直都构建成功,今天突然报错:Jenkins Build step 'Execute shell' marked build as failure 问题原因: By defa ...

  10. Linux 多网卡bonding

    bonding 将多块网卡绑定同一IP地址对外提供服务,可以实现高可用或者负载均衡.直接给两块网卡设置同一IP 地址是不可以的.通过 bonding,虚拟一块网卡对外提供连接,物理网卡的被修改为相同的 ...