C++学习之算术运算符使用详解

目录
  • 1. 前言
  • 2. 运算符种类
  • 3. 算术运算符
    • 3.1 功能描述
    • 3.2 运算符重载问题
    • 3.3 两数相除的问题
    • 3.4 关 于/和%运算符的正、负问题
    • 3.5 数据溢出问题
    • 3.6 类型转换
    • 3.7 {}赋值语法
    • 3.8 强制类型转换
    • 3.9 auto 语法
  • 4. 总结

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。这些转换被称为整型提升。
  • 浮点提升:整型类型自动向浮点类型转换,如 int向 double转换。这种转换是不会存在数据丢失问题,但会产生空间浪费。
  • 向下缩窄: 当目标类型小于原类型时,如double 向 int转换,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。用于执行只有一种用途的类型转换,即改变值为 const 或volatile
  • 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++学习之算术运算符使用详解的详细内容,更多关于C++算术运算符的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++算术运算符与类型转换

    目录 1.算术运算符 2.优先级 3.类型转换 初始化和赋值时的转换 使用花括号进行转换 4.表达式中转换 5.强制类型转换 1.算术运算符 C++当中提供5种基础的算术运算符:加法.减法.乘法.除法和取模. 我们来看下代码: int a = 10, b = 3; cout << a + b << endl; // 13 cout << a - b << endl; // 7 cout << a * b << endl; // 30

  • 一文详解C++中运算符的使用

    目录 一.算术运算符 二.关系运算符 三.逻辑运算符 四.位运算符 五.赋值运算符 六.杂项运算符 一.算术运算符 运算符 描述 + 把两个操作数相加 - 从第一个操作数中减去第二个操作数 * 把两个操作数相乘 / 分子除以分母 % 取模运算符,整除后的余数 ++ 自增运算符,整数值增加 1 – 自减运算符,整数值减少 1 通过下面的例子可以让我们更好的理解C++中的运算符的意义与使用方法. #include <iostream> using namespace std; int main()

  • C++超详细讲解运算符重载

    目录 概念 赋值运算符重载 const成员 取地址及const取地址操作符重载 概念 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似. 函数名字为:关键字operator后面接需要重载的运算符符号. 函数原型:返回值类型 operator操作符(参数列表) 需要注意的几点: 不能通过连接其他符号来创建新的操作符:比如operator@,必须是已有的操作符: 重载操作符必须有一个类类型

  • 详解C++中递增运算符重载的实现

    目录 递增运算符 递增运算符重载的实现 左移运算符重载 前置递增运算符重载 后置递增运算符重载 递增运算符 形如"a++"."++a".如果a初始值为1,那么"a++"结果为1,但是实际上a等于2,先读取再加1:"++a"结果为2,实际也是2.这是对于基本运算类型,那么递增运算符重载的目的就是对于对象的属性也可以直接进行前置递增和后置递增. 由于成员函数里重载写的内容少,就详细分析成员函数实现递增运算符重载 递增运算符重载的实

  • C++简单又好用的基本运算符重载

    目录 运算符重载概念 加号运算符重载 成员函数实现 全局函数实现 运算符实现函数重载 总结 运算符重载概念 对已有的运算符进行重新定义,赋予其另外一种功能,以适应不同的数据类型 我们知道已有的运算符有'+'.'-'.'*'.'/'等,这些运算符对于内置数据类型可以直接使用,例如int.float.double.char等等.但是如果我们定义一个类,想实现类中对象属性的加减乘除运算,该怎么实现呢?那就用到运算符重载的知识点了. 加号运算符重载 学会一个顶四个,这篇博文只举例加号运算符重载 成员函数

  • C++学习之算术运算符使用详解

    目录 1. 前言 2. 运算符种类 3. 算术运算符 3.1 功能描述 3.2 运算符重载问题 3.3 两数相除的问题 3.4 关 于/和%运算符的正.负问题 3.5 数据溢出问题 3.6 类型转换 3.7 {}赋值语法 3.8 强制类型转换 3.9 auto 语法 4. 总结 1. 前言 编写程序时,数据确定后,就需要为数据提供相应的处理逻辑(方案或算法).所谓逻辑有2种存在形态: 抽象形态:存在于意识形态,强调思考过程,与具体的编程语言无关. 具体形态:通过代码来实现.需要使用表达式描述完整

  • Python算术运算符实例详解

    Python算术运算符 以下假设变量a为10,变量b为20: 运算符 描述 实例 + 加 - 两个对象相加 a + b 输出结果 30 - 减 - 得到负数或是一个数减去另一个数 a - b 输出结果 -10 * 乘 - 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 200 / 除 - x除以y b / a 输出结果 2 % 取模 - 返回除法的余数 b % a 输出结果 0 ** 幂 - 返回x的y次幂 a**b 输出结果 20 // 取整除 - 返回商的整数部分 9//2

  • Go语言学习之运算符使用详解

    目录 1.算术运算符 2.关系运算符 3.逻辑运算符 4.位运算符 5.赋值运算符 6.特殊运算符 1.算术运算符 很常规,和java一样. 样例代码如下 // 算术运算符 func base() { a := 1 b := 20 c := 31 d := -1 fmt.Printf(" + -> %d\n", a+b) fmt.Printf(" - -> %d\n", b-a) fmt.Printf(" * -> %d\n",

  • Spring学习笔记1之IOC详解尽量使用注解以及java代码

    在实战中学习Spring,本系列的最终目的是完成一个实现用户注册登录功能的项目. 预想的基本流程如下: 1.用户网站注册,填写用户名.密码.email.手机号信息,后台存入数据库后返回ok.(学习IOC,mybatis,SpringMVC的基础知识,表单数据验证,文件上传等) 2.服务器异步发送邮件给注册用户.(学习消息队列) 3.用户登录.(学习缓存.Spring Security) 4.其他. 边学习边总结,不定时更新.项目环境为Intellij + Spring4. 一.准备工作. 1.m

  • Java web基础学习之开发环境篇(详解)

    Tomcat + Eclipse添加Java EE插件 因为之前进行Java SE学习已经配置了JDK,安装了Eclipse for Java SE,所以选择了在Eclipse上添加插件的方式来配置Web开发环境 Tomcat是免安装版,直接解压即可: Eclipse中"帮助-安装新软件",work with处选择Mars - http://download.eclipse.org/releases/mars(注意对应自己版本): 选择Web.Java EE那个选项进行安装即可,如果报

  • kotlin 官方学习教程之基础语法详解

    kotlin 官方学习教程之基础语法详解 Google 在今天的举行了 I/O 大会,大会主要主要展示内有容 Android O(Android 8.0)系统.Google Assistant 语音助手.Google 智能音箱.人工智能.机器学习.虚拟现实等.作为一个 Android 开发者,我关心的当然是 Android O(Android 8.0)系统了,那么关于 Android O 系统的一个重要消息是全面支持 Kotlin 编程语言,使得 Kotlin 成为了 Android 开发的官方

  • java学习笔记之DBUtils工具包详解

    DBUtils工具包 一.介绍 DBUtils是Apache组织开源的数据库工具类. 二.使用步骤 ①.创建QueryRunner对象 ②.调用update()方法或者query()方法执行sql语句 三.构造方法及静态方法 QueryRunner类 1.构造方法 ①.无参构造 QueryRunner qr =new QueryRunner(); 使用无参构造的时候,调用update方法和query方法时就需要使用带Connection 类型参数的重载形式 ②.有参构造 QueryRunner

  • ES6学习教程之Promise用法详解

    前言 promise用了这么多年了,一直也没有系统整理过.今天整理整理promise的相关东西,感兴趣的可以一起看一看.我尽量用更容易理解的语言来剖析一下promise 我准备分两篇文章来说明一下promise 一篇来理解和使用promise(本篇) 另一篇来从promise使用功能的角度来剖析下promise的源码(下一篇) 1.什么是Promise 我的理解是:实现让我们用同步的方式去写异步代码的一种技术.是异步解决方案的一种. 他可以将多个异步操作进行队列化,让它们可以按照我们的想法去顺序

  • R语言学习笔记之lm函数详解

    在使用lm函数做一元线性回归时,发现lm(y~x+1)和lm(y~x)的结果是一致的,一直没找到两者之间的区别,经过大神们的讨论和测试,才发现其中的差别,测试如下: ------------------------------------------------------------- ------------------------------------------------------------- 结果可以发现,两者的结果是一样的,并无区别,但是若改为lm(y~x-1)就能看出+1和

  • Python的运算符重载详解

    一.前言 运算符重载:为运算符定义方法 所谓重载,就是赋予新的含义同一个运算符可以有不同的功能 二.重载作用 让自定义的实例像内建对象一样进行运算符操作让程序简介易读对自定义对象将运算符赋予新的规则 运算符和特殊方法 运算符重载 # @function:运算符重载 # @Description: 一只萤火虫 class MyInteger: """ 创建一个自定义的整数类型 """ def __init__(self, data=0): # 1.

随机推荐