C++编程:运算符

2023-05-28 0 960

有了统计数据后,就能对统计数据第一类展开各式各样排序了。在C语言中,能透过“操作符”来则表示想展开的排序。

1 函数和操作符

1.1 基本原理

在流程中,三个或数个演算第一类的女团叫作“函数”(expression),他们能把它看作用以做排序的“等式”。对三个函数展开排序,能获得三个结论,有时候也把它叫作函数的值。

后面说到的字面额自变量和表达式,是最单纯的函数;函数的结论是字面额和表达式的值。而数个字面额和表达式,能透过许多记号相连女团在一同,则表示展开适当的排序,这就能获得更为繁杂的函数,比如说 a + 1。像“+”那些记号就被叫作“操作符”(operator)。

C++中表述的操作符,能是像“+”这种相连三个第一类,称作“相互依赖操作符”;也能只促进作用于三个第一类,称作“十元操作符”。除此之外,除了三个较为特定的操作符能促进作用于四个第一类,那是广济操作符了。

1.2 演算错误率和交换律

假如在三个函数中,采用数个操作符女团了数个演算第一类,就形成了更为繁杂的“A43EI235E函数”,比如说 a + 1 – b。对A43EI235E函数,很或许他们如果分阶段来做排序;而排序次序,是由简而言之的“错误率”和“交换律”确认的。

单纯来说,是对完全相同的操作符突显完全相同的“错误率”,他们会优先选择继续执行高错误率的演算、再继续执行低错误率的演算。假如错误率完全相同,就依照“交换律”来下定决心继续执行次序。这只不过跟微积分的综合性微积分公式是那样的,他们会表述算数的错误率要低于以此类推,Kendujhar演算从左到右,因此对微积分公式:

1 + 2 – 3 × 4

他们会先排序高错误率的 3×4,然后依照从左到右的结合次序排序1+2,最后做减法。除此之外,假如有括号,那就要先把括起来的部分当成三个整体先做排序,然后再考虑括号外的结合次序,这一点在C++函数中同样适用。

2. 算术演算

最单纯的操作符,是则表示算术排序的以此类推算数,这一类被称作“算术操作符”。C++支持的算术操作符如下:

C++编程:运算符

这里需要注意的是,同三个操作符,在完全相同的场合可能表达完全相同的含义。比如说“-”,能是“减号”也能是“负号”:假如直接放在三个函数后面,是对函数的结论取负数,这是十元操作符;假如相连三个函数,是两者结论相减,是相互依赖操作符。

算术操作符相关规则如下:

十元操作符(正负号)错误率最高;接下来是乘、除和取余;最后是以此类推;

算术操作符满足左交换律,也是说完全相同错误率的操作符,将从左到右按次序展开女团;

算术操作符能用来处理任意算术类型的统计数据第一类;

完全相同类型的统计数据第一类展开排序时,较小的整数类型会被“提升”为较大的类型,最终转换成同一类型展开排序;

对除法演算“/”,继续执行排序的结论跟操作数的类型有关。假如它的三个操作数(也是被除数和除数)都是整数,那么获得的结论也只能是整数,小数部分会直接舍弃,这叫“整数除法”;当至少有三个操作数是浮点数时,结论就会是浮点数,保留小数部分;

对取余演算“%”(或者叫“取模”),两个操作数必须是整数类型;

// 除法

int a = 20, b = 6;

cout << ” a / b = ” << a / b << endl;

cout << ” -a / b = ” << -a / b << endl; // 负数向0取整

float a2 = 20;

cout << ” a2 / b = ” << a2 / b << endl;

// 取模

cout << ” a % b = ” << a % b << endl;

cout << ” -a % b = ” << -a % b << endl;

在这里,同样是除法操作符“/”,针对完全相同类型的统计数据第一类,只不过会做完全相同的处理。采用完全相同的记号、根据上下文来继续执行完全相同操作,这是C++提供的一大特色功能,叫作“操作符重载”(operator overloading)。

3. 赋值

将三个函数的结论,传递给某个统计数据第一类保存起来,这个过程叫作“赋值”。

3.1 赋值操作符

在C++中,用等号“=”则表示三个赋值操作,这里的“=”是赋值操作符。需要注意的是,赋值操作符的左边,必须是三个可修改的统计数据第一类,比如说假设他们已经表述了三个int类型的表达式a,那么

a = 1;

这种赋值是对的,但

1 = a;

是错误的。因为a是三个表达式,能赋值;而 1只是三个字面额自变量,不能再对它赋值。

int a, b;

a = 1;

//1 = a; // 错误:函数必须是可修改的左值

a = b + 5;

//b + 5 = a; // 错误:函数必须是可修改的左值

const int c = 10;

//c = a + b; // 错误:函数必须是可修改的左值

因此像表达式a这样的能赋值的演算第一类,在C++中被叫作“左值”(lvalue);对应的,放在赋值语句右面的函数是“右值”(rvalue)。

赋值演算有以下许多规则:

赋值演算的结论,是它左侧的演算第一类;结论的类型是左侧演算第一类的类型;

假如赋值操作符两侧第一类类型完全相同,就把右侧的第一类转换成左侧第一类的类型;

C++ 11新标准提供了一种新的语法:用花括号{}括起来的数值列表,能作为赋值右侧第一类。这种就能非常方便地对三个数组赋值了;

赋值演算满足右交换律。也是说能在一条语句中连续赋值,结合次序是从右到左;

赋值操作符错误率较低,一般都会先继续执行其它操作符,最后做赋值;

a = {2};

int arr[] = {1,2,3,4,5}; // 用花括号对数组赋值

a = b = 20; // 连续赋值

3.2 A43EI235E赋值操作符

实际应用中,他们经常需要把一次排序的结论,再赋值给参与演算的某三个表达式。最单纯的例子是数个数求和,比如说他们要排序a、b、c的和,那么能专门表述三个表达式sum,用以保存求和结论:

int sum = a; // 初始值是a

sum = sum + b; // 叠加b

sum = sum + c; // 叠加c

要注意赋值操作符“=”完全不是微积分上“等于”的意思,因此上面的赋值语句sum = sum + b; 说的是“排序sum + b的结论,然后把它再赋值给sum”。

为了更为简洁,C++提供了一类特定的赋值操作符,能把要继续执行的算术演算“+”跟赋值“=”结合在一同,用三个操作符“+=”来则表示;这是“A43EI235E赋值操作符”。

A43EI235E赋值一般结合的是算术操作符或者位操作符。每种操作符都有对应的女团形式:

C++编程:运算符

关于位操作符,他们会在稍后介绍。

这种上面的代码能改写为:

int sum = a; // 初始值是a

sum += b; // 完全等价于 sum = sum + b;

sum += c;

3.3 递增递减操作符

C++为统计数据第一类的“加一”“减一”操作,提供了更为简洁的表达方式,这是递增和递减操作符(也叫“自增”“自减”操作符)。“递增”用三个加号“++”则表示,则表示“第一类值加一,再赋值给原第一类”;“递减”则用三个减号“–”则表示。

++a; // a递增,相当于 a += 1;

–b; // b递减,相当于 b -= 1;

递增递减操作符各自有两种形式:“前置”和“后置”,也是说写成“++a”和“a++”都是能的。它们都则表示“a = a + 1”,区别在于函数返回的结论完全相同:

前置时,第一类先加1,再将更新后的第一类值作为结论返回;

后置时,第一类先将原始值作为结论返回,再加1;

这要特别注意:假如他们单独采用递增递减操作符,那前置后置效果都那样;但假如演算结论还要进一步做排序,两者就有明显完全相同了。

int i = 0, j;

j = ++i; // i = 1,j = 1

j = i–; // i = 0, j = 1

在实际应用中,一般都是希望用改变后的第一类值;因此为了避免混淆,他们通常会统一采用前置的写法。

4. 关系和逻辑演算

在流程中,不可缺少的一类演算是逻辑和关系演算,因为他们往往需要表述“在某种条件发生时,继续执行某种操作”。判断条件是否发生,这是三个典型的逻辑判断;获得的结论或者为“真”(true),或者为“假”。很或许,这类演算的结论如果是布尔类型。

4.1 关系操作符

最单纯的一种条件,是判断三个算术第一类的大小关系,对应的操作符称作“关系操作符”。包括:大于“>”、小于“<”、等于“==”、不等于“!=”、大于等于“>=”、小于等于“<=”。

C++编程:运算符

这里要注意区分的是,在C++语法中一个等号“=”则表示的是赋值,三个等号“==”才是真正的“等于”。

1 < 2; // true

3 >= 5; // false

10 == 4 + 6; // true

(10 != 4) + 6; // 7

关系操作符的相关规则:

算术操作符的错误率低于关系操作符,而假如加上括号就能调整排序次序;

关系操作符的返回值为布尔类型,假如参与算术排序,true的值为1,false的值为0;

4.2 逻辑操作符

三个关系操作符的结论是三个布尔类型(ture或者false),就能则表示三个条件的判断;假如需要数个条件的叠加,就能用逻辑“与或非”将那些布尔类型女团起来。这种的操作符叫作“逻辑操作符”。

逻辑非(!):一元操作符,将演算第一类的值取反后返回,真值反转;

逻辑与(&&):相互依赖操作符,三个演算第一类都为true时结论为true,否则结论为false;

逻辑或(||):相互依赖操作符,三个演算第一类只要有三个为true结果就为true,都为false则结论为false;

1 < 2 && 3 >= 5; // false

1 < 2 || 3 >= 5; // true

!(1 < 2 || 3 >= 5); // false

他们能把逻辑操作符和关系操作符的用法、错误率和交换律总结如下(从上到下错误率递减):

C++编程:运算符

这里需要注意的规则有:

假如将三个算术类型的第一类作为逻辑操作符的操作数,那么值为0则表示false,非0值则表示true;

逻辑与和逻辑或有三个演算第一类,在排序时都是先求左侧第一类的值,再求右侧第一类的值;假如左侧第一类的值已经能下定决心最终结论,那么右侧就不会继续执行排序:这种策略叫作“短路求值”;

i = -1;

1 < 2 && ++i; // false

cout << ” i = ” << i << endl; // i = 0

1 < 2 || ++i; // true

cout << ” i = ” << i << endl; // i = 0

4.3 条件操作符

C++还从C语言继承了三个特定的操作符,叫作“条件操作符”。它由“?”和“:”三个记号组成,需要四个演算函数,形式如下:

条件判断函数 ? 函数1 : 表达式2

它的含义是:排序条件判断函数的值,假如为true就继续执行函数1,返回求值结论;假如为false则跳过函数1,继续执行函数2,返回求值结论。这也是C++中唯一的三个广济操作符。

i = 0;

cout << ((1 < 2 && ++i) ? “true” : “false”) << endl;

l 条件操作符的错误率较为低,因此输出的时候需要加上括号

l 条件操作符满足右交换律

事实上,条件操作符等同于流程控制中的分支语句if…else…,只用一条语句就能实现按条件分支处理,这就让代码更为简洁。关于分支语句,他们会在后面详细介绍。

5. 位操作符

之前介绍的所有操作符,主要都是针对算术类型的统计数据第一类展开操作的;所有的算术类型,占用的空间都是以字节(byte,8位)作为单位来衡量的。在C++中,除了一类非常底层的操作符,能直接操作到具体的每一位(bit)统计数据,这是“位操作符”。

位操作符能分为两大类:移位操作符,和位逻辑操作符。下面列出了所有位操作符的错误率和用法。

C++编程:运算符

5.1移位操作符

算术类型的统计数据第一类,都能看做是一组“位”的集合。那么利用“移位操作符”,就能让演算第一类的所有位,整体移动指定的位数。

移位操作符有两种:左移操作符“<<”和右移操作符“>>”。这个记号他们并不陌生,之前做输入输出操作的时候用的是它,不过那是标准IO库里表述的操作符重载版本。

下面是移位操作符的三个具体案例:

C++编程:运算符

较小的整数类型(char、short以及bool)会自动提升成int类型再做移位,获得的结论也是int类型

左移操作符“<<”将操作数左移后,在右侧补0;

右移操作符“>>”将操作数右移后,对于无记号数就在左侧补0;对有记号数的操作则要看运行的机器环境,有可能补记号位,也有可能直接补0;

由于有记号数右移结论不确认,一般只对无记号数继续执行位移操作;

unsigned char bits = 0xb5; // 181

cout << hex; // 以十六进制显示

cout << “0xb5 左移2位:” << (bits << 2) << endl; // 0x 0000 02d4

cout << “0xb5 左移8位:” << (bits << 8) << endl; // 0x 0000 b500

cout << “0xb5 左移31位:” << (bits << 31) << endl; // 0x 8000 0000

cout << “0xb5 右移3位:” << (bits >> 3) << endl; // 0x 0000 0016

cout << dec;

cout << (200 << 3) << endl; // 乘8操作

cout << (-100 >> 2) << endl; // 除4操作,一般右移是补记号位

5.2 位逻辑操作符

排序机存储的每三个“位”(bit)都是二进制的,有0和1两种取值,这跟布尔类型的真值表达非常类似。于是自然能想到,三个位上的“0”或“1”都能继续执行类似逻辑演算的操作。

位逻辑操作符有:按位取反“~”,位与“&”,位或“|”和位异或“^”。

按位取反“~”:十元操作符,类似逻辑非。对每个位取反值,也是把1置为0、0置为1;

位与“&”:相互依赖操作符,类似逻辑与。三个数对应位上都为1,结论对应位为1;否则结论对应位为0;

位或“|”:相互依赖操作符,类似逻辑或。三个数对应位上只要有1,结论对应位就为1;假如全为0则结论对应位为0;

位异或“^”:三个数对应位完全相同,则结论对应位为0;完全相同则结论对应位为0;

下面是位逻辑操作符的三个具体案例:

C++编程:运算符

// 位逻辑演算

cout << (~5) << endl; // ~ (0… 0000 0101) = 1… 1111 1010, -6

cout << (5 & 12) << endl; // 0101 & 1100 = 0100, 4

cout << (5 | 12) << endl; // 0101 | 1100 = 1101, 13

cout << (5 ^ 12) << endl; // 0101 & 1100 = 1001, 9

6. 类型转换

在C++中,完全相同类型的统计数据第一类,是能放在一同做排序的。这就要求必须有三个机制,能让有关联的两种类型能互相转换。在上一章已经介绍过表达式赋值时的自动类型转换,接下来他们会对类型转换做更详细的展开。

6.1 隐式类型转换

大多数情况,C++编译器能自动对类型展开转换,不需要他们干涉,这种方式叫作“隐式类型转换”。

隐式类型转换主要发生在算术类型之间,基本思路是将长度较小的类型转换成较大的类型,这种能避免丢失精度。隐式类型转换不仅能在表达式赋值时发生,也能在演算函数中出现。例如:

short s = 15.2 + 20;

cout << ” s = ” << s << endl; // s = 35

cout << ” 15.2 + 20 结论长度为:” << sizeof(15.2 + 20) << endl;

cout << ” s 长度为:” << sizeof(s) << endl;

对这条赋值语句,右侧是三个字面额自变量相加,而且类型完全相同:15.2是double类型,20是int类型。当它们相加时,会将int类型的20转换为double类型,然后继续执行double的加法操作,获得35.2。

这个结论用以初始化表达式s,由于s是short类型,因此还会把double类型的结论35.2再去掉小数部分,转换成short类型的35。因此s最终的值为35。

隐式类型转换的一般规则能总结如下:

在大多数算术演算中,较小的整数类型(如bool、char、short)都会转换成int类型。这叫作“整数提升”;(而对wchar_t等较大的扩展字符类型,则根据需要转换成int、unsigned int、long、unsigned long、long long、unsigned long long中能容纳它的最小类型)

当函数中有整型也有浮点型时,整数值会转换成适当的浮点类型;

在条件判断语句中,其它整数类型会转换成布尔类型,即0为false、非0为true;

初始化表达式时,初始值转换成表达式的类型;

在赋值语句中,右侧第一类的值会转换成左侧第一类的类型;

此外,要尽量避免将较大类型的值赋给较小类型的表达式,这种很容易出现精度丢失或者统计数据溢出。

s = 32767;

cout << ” s + 1 = ” << s + 1 << endl;

short s2 = s + 1;

cout << ” s2 = ” << s2 << endl;

除此之外还要注意,假如希望判断三个整型表达式a是否在某个范围(0, 100)内,不能直接写:0 < a < 100;

由于小于操作符“<”满足左交换律,要先排序0 < a,获得三个布尔类型的结论,再跟后面的100展开较为。此时布尔类型做整数提升,不管值是真(1)还是假(0),都会满足 < 100 的判断,因此最终结论一定是true。

要想获得正确的结论,需要将两次关系判断拆开,写成逻辑与的关系。

a = -1;

0 < a < 100; // 不论a取什么值,总是true

0 < a && a < 100; // false

6.2 强制类型转换

除去自动展开的隐式类型转换,他们也能显式地要求编译器对统计数据第一类的类型展开更改。这种转换叫作“强制类型转换”(cast)。

比如说对除法演算,他们知道整数除法和浮点数除法是完全相同的。假如希望对一组整数求三个平均数,直接相加后除以个数是无法获得想的结论的:

// 求平均数

int total = 20, num = 6;

double avg = total / num;

cout << ” avg = ” << avg << endl; // avg = 3

因为三个int类型的数相除,继续执行的是整数除法,获得3;再转换成double类型对avg做初始化,获得是3.0。假如想更准确的结论,就必须将int类型强制转换成double,做浮点数除法。

C++中能采用完全相同的方式展开强制类型转换。

(1)C语言风格

最经典的强转方式来自C语言,格式如下:

(类型名称) 值

把要强制转成的类型,用三个小括号括起来,放到要转换的第一类值后面就能了。

(2)C++函数调用风格

这种方式跟C语言的强转类似,只不过看起来更像是调用了三个函数:

类型名称 (值)

要转成的类型名就像是三个函数,调用的时候,后面小括号里是传递给它的参数。

(3)C++强制类型转换操作符

C++还引入了4个强制类型转换操作符,这种新的转换方式比前两种传统方式要求更为严格。通常在类型转换中用到的操作符是static_cast,用法如下:

static_cast<类型名称> (值)

static_cast操作符后要跟三个尖括号,里面是要转换成的类型。

有了那些强转的方式,就能解决之前求平均数的问题了:

// C语言风格

cout << ” avg = ” << (double) total / num << endl;

// C++函数风格

cout << ” avg = ” << double (total) / num << endl;

// C++强转演算符

cout << ” avg = ” << static_cast<double>(total) / num << endl;

强制类型转换会干扰正常的类型检查,带来很多风险,因此通常要尽量避免采用强制类型转换。

举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务