C++中的运算符和流程控制语句
- 一、运算符
- 1. C++和Java在通用运算符中的不同之处对比
- 2. C++中的位运算符
- 2.1 移位运算符
- 2.2 位逻辑运算符
- 3. 运算时的类型转换总结
- 3.1 隐式类型转换
- 3.2 显式类型转换(强制类型转换)
- 4. 注意
- 二、流程控制语句
- 1. C++和Java在通用流程控制语句中的不同之处对比
- 2. 跳转语句goto
一、运算符
1. C++和Java在通用运算符中的不同之处对比
运算符 | C++ | Java |
---|---|---|
取余运算% | 两个操作数必须是整数类型 | 两个操作数可以是浮点型 |
赋值运算= | 运算结果的类型就是左侧运算对象的类型,C++会自动进行隐式转换 | 运算结果的类型如果和左侧运算对象的类型不一致,需要进行显式转换 |
逻辑运算符 | 允许算数类型的数据参与运算 | 只允许布尔类型的数据参与运算 |
2. C++中的位运算符
所有的算术类型,占用的空间都是以字节(byte,8位)作为单位来衡量的。在C++中,还有一类非常底层的运算符,可以直接操作到具体的每一位(bit)数据,这就是位运算符。位运算符可以分为两大类:移位运算符,和位逻辑运算符。下面列出了所有位运算符的优先级和用法:
2.1 移位运算符
算术类型的数据对象,都可以看做是一组位的集合。那么利用“位运算符,就可以让运算对象的所有位,整体移动指定的位数。移位运算符有两种:左移运算符<<
和右移运算符>>
。如下为移位运算符的具体案例:
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操作,一般右移是补符号位
移位运算符的运算规则如下:
- 较小的整数类型(char、short以及bool)会自动提升成int类型再做移位,得到的结果也是int类型
- 左移运算符
<<
将操作数左移之后,在右侧补0; - 右移运算符
>>
将操作数右移之后,对于无符号数就在左侧补0;对于有符号数的操作则要看运行的机器环境,有可能补符号位,也有可能直接补0; - 由于有符号数右移结果不确定,一般只对无符号数执行位移操作;
这里解释一下为什么有符号数右移结果不确定,这主要有两个原因:① 有符号数在进行右移动是补的数据是1还是0要根据该数据是正数还是负数来定,正数补0,负数补1② 相同的数据类型在不同的机器环境上的位数是不同的,可能在机器1上的定义正数转移到机器2上就被理解为负数。
2.2 位逻辑运算符
计算机存储的每一个位(bit)都是二进制的,有0和1两种取值,这跟布尔类型的真值表达非常类似。于是自然可以想到,两个位上的0或1都可以执行类似逻辑运算的操作。位逻辑运算符有:按位取反~
,位与&
,位或|
和位异或^
。
- 按位取反
~
:一元运算符,类似逻辑非。对每个位取反值,也就是把1置为0、0置为1; - 位与
&
:二元运算符,类似逻辑与。两个数对应位上都为1,结果对应位为1;否则结果对应位为0; - 位或
|
:二元运算符,类似逻辑或。两个数对应位上只要有1,结果对应位就为1;如果全为0则结果对应位为0; - 位异或
^
:两个数对应位相同,则结果对应位为0;不同则结果对应位为0;
位逻辑运算的具体案例如下:
// 位逻辑运算
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
3. 运算时的类型转换总结
3.1 隐式类型转换
大多数情况,C++编译器可以自动对类型进行转换,不需要我们干涉,这种方式叫做隐式类型转换。隐式类型转换主要发生在算术类型之间,基本思路就是将长度较小的类型转换成较大的类型,这样可以避免丢失精度。隐式类型转换不仅可以在变量赋值时发生,也可以在运算表达式中出现。
隐式类型转换的一般规则可以总结如下:
- 在大多数算术运算中,较小的整数类型(如bool、char、short)都会转换成int类型。这叫做整数提升;
- 当表达式中有整型也有浮点型时,整数值会转换成相应的浮点类型;
- 在条件判断语句中,其它整数类型会转换成布尔类型,即0为false、非0为true;
- 初始化变量时,初始值转换成变量的类型;
- 在赋值语句中,右侧对象的值会转换成左侧对象的类型;
3.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++中要想进行强制类型转换,主要有以下三种方式:
- C语言风格:
(类型名称) 值
- C++函数调用风格(推荐):
类型名称 (值)
- C++强制类型转换运算符:
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;
4. 注意
- 要尽量避免将较大类型的值赋给较小类型的变量,这样很容易出现精度丢失或者数据溢出。
- 如果希望判断一个整型变量a是否在某个范围(0, 100)内,不能直接写:0 < a < 100;由于小于运算符
<
满足左结合律,要先计算0 < a,得到一个布尔类型的结果,再跟后面的100进行比较。此时布尔类型做整数提升,不管值是真(1)还是假(0),都会满足 < 100 的判断,因此最终结果一定是true。要想得到正确的结果,需要将两次关系判断拆开,写成逻辑与的关系。
二、流程控制语句
1. C++和Java在通用流程控制语句中的不同之处对比
语句 | C++ | Java |
---|---|---|
switch | C++中switch中的条件类型必须是整型 | Java中switch中的条件表类型必须是char、byte、short、int、String、enum中的一种 |
2. 跳转语句goto
goto语句表示无条件地跳转到程序中的另一条语句。goto的语法形式为:
goto 标签;
这里的标签可以认为是一条语句的名字,跟变量类似,只不过它是指代一条语句的标识符。定义标签也非常简单,只要在一条语句前写出标识符,然后跟上冒号就可以了,比如:
begin: int a = 0;
下面是一个具体的例子:
int x = 0;
cout << "程序开始..." << endl;
begin:
do
{
cout << " x = " << ++x << endl;
} while (x < 10);
if (x < 15) {
cout << "回到原点!" << endl;
goto begin;
}
cout << "程序结束!" << endl;
由于goto可以任意跳转,所以它非常灵活,也非常危险。一般在代码中不要使用goto。