C++ - 运算符和控制语句

算术运算符:

算术运算符

  • 注意:同一个运算符,在不同的场合可能表达不同的含义。比如“-”,可以是“减号”也可以是“负号”:如果直接放在一个表达式前面,就是对表达式的结果取负数,这是一元运算符;如果连接两个表达式,就是两者结果相减,是二元运算符。

  • 一元运算符(正负号)优先级最高;接下来是乘、除和取余;最后是加减;

  • 算术运算符满足左结合律,也就是说相同优先级的运算符,将从左到右按顺序进行组合;

  • 算术运算符可以用来处理任意算术类型的数据对象;

  • 不同类型的数据对象进行计算时,较小的整数类型会被“提升”为较大的类型,最终转换成同一类型进行计算;

  • 对于除法运算“/”,执行计算的结果跟操作数的类型有关。如果它的两个操作数(也就是被除数和除数)都是整数,那么得到的结果也只能是整数,小数部分会直接舍弃,这叫“整数除法”;当至少有一个操作数是浮点数时,结果就会是浮点数,保留小数部分;

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 除法
    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)。

赋值运算符:

  1. 将一个表达式的结果,传递给某个数据对象保存起来,这个过程叫做“赋值”。

  2. 在C++中,用等号“=”表示一个赋值操作,这里的“=”就是赋值运算符。需要注意的是,赋值运算符的左边,必须是一个可修改的数据对象

  3. 如果赋值运算符两侧对象类型不同,就把右侧的对象转换成左侧对象的类型

  4. 赋值运算符优先级较低,一般都会先执行其它运算符,最后做赋值

  5. C++提供了一类特殊的赋值运算符,可以把要执行的算术运算“+”跟赋值“=”结合在一起,用一个运算符“+=”来表示;这就是“复合赋值运算符”

    赋值运算符

递增递减运算符:

  1. “递增”用两个加号“++”表示,表示“对象值加一,再赋值给原对象”;“递减”则用两个减号“–”表示。

    1
    2
    ++a;    // a递增,相当于 a += 1;
    --b; // b递减,相当于 b -= 1;
  2. ++i:先加减,后使用; i++:先使用,后加减

关系运算符:

关系运算符

  • 注意:在C++语法中一个等号“=”表示的是赋值,两个等号“==”才是真正的“等于”
  • 比较运算的结果只有两个取值,要么是真(非0 表示,默认使用1),要么是假(0 表示)。

逻辑运算符:

逻辑运算符

  • 如果左侧对象的值已经能决定最终结果,那么右侧就不会执行计算:这种策略叫做“短路求值”;

条件运算符:

  1. ()?():()
  2. 格式:(条件表达式)? 表达式1:表达式2
  3. 说明:条件表达式是如果为 true (非0值),就执行表达式1,否则执行表达式2。

位运算符:

位运算符

  • 结合赋值运算符的经验,这里有:<<= 、 >>= 、 &= 、 ^= 等
  • 左移(<<):
    • 运算规则:在一定范围内,数据每向左移动一位,相当于原数据*2。(正数、负数都适用)
    • 当左移的位数n超过该数据类型的总位数时,相当于左移(n-总位数)位
    • 3<<4 类似于 32的4次幂 => 316 => 48
    • -3<<4 类似于 -32的4次幂 => -316 => -48
  • 右移(>>):
    • 运算规则:在一定范围内,数据每向右移动一位,相当于原数据/2。(正数、负数都适用)
    • 如果不能整除,向下取整
    • 右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不一样的结果。
    • 69>>4 类似于 69/2的4次幂 = 69/16 =4
    • 69>>4 类似于 69/2的4次幂 = 69/16 =4
  • 异或(^):相同为0,不同为1
  • 对于一个二进制数a,使第 n 位不论是几都变成0,其它位不变:a=a&(~(1<<n));表示1左移n位,然后取反
  • 对于一个二进制数a,使第 n 位不论是几都变成1,其它位不变:a=a|(1<<n)

运算符优先级:

运算符优先级

  • 分不清优先级的时候,可以采用()来解决
  • 逗号运算符:(),() 输出结果是后面表达式的结果

if-else选择语句:

  • if语句是互斥的
  • if语句可以嵌套新的if语句

switch语句:

  1. switch 语句用于判断条件有多个常量结果的情况。

  2. 执行过程:

    1. 第1步:根据switch中表达式的值,依次匹配各个case。如果表达式的值等于某个case中的常量值,则执行对应case中的执行语句。
    2. 第2步:执行完此case的执行语句以后,如果遇到break,则执行break并跳出当前的switch-case结构,如果没有遇到break,则会继续执行当前case之后的其它case中的执行语句。—>case穿透,直到遇到break关键字或执行完所有的case及default的执行语句,跳出当前的switch-case结构
  3. 使用注意点:

    1. case句中的值必须是常量,不能是变量名或不确定的表达式值或范围。
    2. 同一个switch语句,所有case子句中的常量值互不相同。
    3. 如果没有break,程序会顺序执行到switch结尾;从使用频率说,一般switch-case结构中,都需要编写break。
    4. default子句是可选的。同时,位置也是灵活的。当没有匹配的case时,执行default语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
switch(表达式){
case 常量值1: //常量只能是字符型或整型的常量
语句块1;
break; //如果没有break,会继续执行下一个
case 常量值2:
语句块2;
break;
.......
case 常量值n:
语句块n;
break;
default: //都没有匹配到 执行这个
语句块n+1;
}
switch(表达式){
case 常量值1:
语句块1;
break;
case 常量值2:
case 常量值3:
case 常量值4:
case 常量值5://当上下输出结果相同时,上边可以省略不写
语句;
break;
.......
case 常量值n:
语句块n;
break;
default: //都没有匹配到 执行这个
语句块n+1;
}

for循环语句:

  1. 循环语句具有在某些条件满足的情况下,反复执行特定代码的功能

  2. 语法格式:

    1
    2
    3
    for (①初始化部分; ②循环条件部分; ④迭代部分){
    ③循环体部分;

    • for(;;)中的两个;不能多也不能少,而其他部分可以不写
    • ①初始化部分,用于初始化循环变量,只执行一次。可以声明多个变量,但必须是同一个类型,用逗号分隔
    • ②循环条件部分,只要为 true ,就会不断执行循环体;当值为false时,退出循环
    • ④迭代部分,每轮循环结束后执行,使得循环变量发生变化。可以有多个变量更新,用逗号分隔

while循环语句:

  • 语法格式:

    1
    2
    3
    4
    5
    ①初始化部分
    while(②循环条件部分){
    ③循环体部分;
    ④迭代部分;
    }
  • 说明:

    1. while(循环条件部分)中循环条件为非零值,表示true、真;为零值,表示false、假。
    2. 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
    3. for循环和while循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
    4. for循环与while循环的区别:初始化条件部分的作用域不同

do-while循环语句:

  • do-while 结构是 while 的变体,它会先执行一次循环体,然后再判断是否满足条件。如果满足的话,就继续执行循环体,否则跳出循环。

  • 语法格式:

    1
    2
    3
    4
    5
    ①初始化部分;
    do{
    ③循环体部分
    ④迭代部分
    }while(②循环条件部分); //分号不要忘记
  • 说明:

    1. do{}while();最后有一个分号
    2. do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的
    3. 循环的三个结构for、while、do-while三者是可以相互转换的。

三种循环结构:

  1. 三种循环结构都具有四个要素:
    • 初始化部分—–>只执行一次
    • 循环条件部分—>一定是布尔类型的结果
    • 循环体部分——>反复执行的代码
    • 迭代部分———>相关的变量会做更新、迭代
  2. 从循环次数角度分析:
    • do-while循环至少执行一次循环体语句。
    • for和while循环先判断循环条件语句是否成立,然后决定是否执行循环体。
  3. 如何选择:
    • 遍历有明显的循环次数(范围)的需求,选择for循环
    • 遍历没有明显的循环次数(范围)的需求,选择while循环
    • 如果循环体语句块至少执行一次,可以考虑使用do-while循环
    • 本质上:三种循环之间完全可以互相转换,都能实现循环的功能

break和continue关键字:

  • 使用说明:

    break

goto关键字:

  • 使用goto,可以实现无条件的语句的转移。

  • 格式:

    1. goto 标号(标签);
    2. 其中,标号,属于标识符,以“:”为标记,位于某语句前面。
    3. 执行 goto 语句后,程序将跳转到指定标号处执行。这样可以随意将控制转移到程序中的任意一条语句上,然后执行它。
  • 举例:

    1
    2
    3
    4
    5
    6
    7
    int main() {

    loop_label:printf("Hello, world!\n");
    goto loop_label;

    return 0;
    }
    • loop_label是一个标签名,可以放在正常语句的前面。程序执行到 goto 语句,就会跳转到它指定的标签名位置继续执行。因此,上面的代码会产生无限循环。
    • 实际使用中,goto语句通常与条件语句配合。可用来实现条件转移,跳出循环体等功能。

参考链接: