• 一零一七、C语言白菜入门--运算符


    C 语⾔的运算符⾮常多,⼀共有 50 多种,大致可以分成这几类。

    目录

    算数运算符

    自增,自减运算符

    关系运算符

    逻辑运算符

    位运算符

    逗号运算符

    运算优先级


    算数运算符

    算术运算符专⻔⽤于算术运算,主要有下⾯⼏种。

     (1) + , -

    + 和 - 既可以作为⼀元运算符,也可以作为⼆元运算符。所谓“⼀元运算符”,指的是只需要⼀个运算数就可以执⾏。⼀元运算符 - ⽤来改变⼀个值的正负号。

    int x = -12;

    上⾯示例中, - 将 12 这个值变成 -12 。

    ⼀元运算符 + 对正负值没有影响,是⼀个完全可以省略的运算符,但是写了也不会报错。

    1. int x = -12;
    2. int y = +x;

    上⾯示例中,变量 y 的值还是 -12 ,因为 + 不会改变正负值。

    ⼆元运算符 + 和 - ⽤来完成加法和减法。

    1. int x = 4 + 22;
    2. int y = 61 - 23;

    (2) *

    运算符 * ⽤来完成乘法。

    1. int num = 5;
    2. printf("%i\n", num * num); // 输出 25

    (3) /

    运算符 / ⽤来完成除法。注意,两个整数相除,得到还是⼀个整数。

    1. float x = 6 / 4;
    2. printf("%f\n", x); // 输出 1.000000

    上⾯示例中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,⽽不是 1.5 。原因 就在于 C 语⾔⾥⾯的整数除法是整除,只会返回整数部分,丢弃⼩数部分。

    如果希望得到浮点数的结果,两个运算数必须⾄少有⼀个浮点数,这时 C 语⾔就会进⾏浮点数除法。

    1. float x = 6.0 / 4; // 或者写成 6 / 4.0
    2. printf("%f\n", x); // 输出 1.500000

    上⾯示例中, 6.0 / 4 表示进⾏浮点数除法,得到的结果就是 1.5 。

    下⾯是另⼀个例⼦。

    1. int score = 5;
    2. score = (score / 20) * 100;

    上⾯的代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为 score / 20 是整除,会得到⼀个整数值 0 ,所以乘以 100 后得到的也是 0 。

    为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。

    score = (score / 20.0) * 100;

    (4) %

    运算符 % 表示求模运算,即返回两个整数相除的余值。这个运算符只能⽤于整数,不能⽤于浮点数。

    int x = 6 % 4; // 2

    负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定。

    1. 11 % -5 // 1
    2. -11 % -5 // -1
    3. -11 % 5 // -1

    上⾯示例中,第⼀个运算数的正负号( 11 或 -11 )决定了结果的正负号。

    (5)赋值运算的简写形式

    如果变量对⾃身的值进⾏算术运算,C 语⾔提供了简写形式,允许将赋值运算符和算术运算符结合成⼀个运算符。

    • +=
    • -+
    • *=
    • /=
    • %=

    下面列举一些例子

    1. i += 3; // 等同于 i = i + 3
    2. i -= 8; // 等同于 i = i - 8
    3. i *= 9; // 等同于 i = i * 9
    4. i /= 2; // 等同于 i = i / 2
    5. i %= 5; // 等同于 i = i % 5

    自增,自减运算符

    C 语⾔提供两个运算符,对变量⾃身进⾏ + 1 和 - 1 的操作。

    1. ++:自增运算符
    2. --:自减运算符
    1. i++; // 等同于 i = i + 1
    2. i--; // 等同于 i = i - 1

    这两个运算符放在变量的前⾯或后⾯,结果是不⼀样的。 ++var 和 --var 是先执⾏⾃增或⾃减操作,再返回操作后 var 的值; var++ 和 var-- 则是先返回操作前 var 的值,再执⾏⾃增或⾃减操作。

    1. int i = 42;
    2. int j;
    3. j = (i++ + 10);
    4. // i: 43
    5. // j: 52
    6. j = (++i + 10)
    7. // i: 44
    8. // j: 54

    上⾯示例中,⾃增运算符的位置差异,会导致变量 j 得到不同的值。这样的写法很容易出现意料之外的结果,为了消除意外,可以改⽤下⾯的写法。

    1. /* 写法⼀ */
    2. j = (i + 10);
    3. i++;
    4. /* 写法⼆ */
    5. i++;
    6. j = (i + 10);

    上⾯示例中,变量 i 的⾃增运算与返回值是分离的两个步骤,这样就清晰很多,也提⾼了代码的可读性。

    关系运算符

    C 语⾔⽤于⽐较的表达式,称为“关系表达式”(relational expression),⾥⾯使⽤的运算符就称为“关系运算符”(relational operator),主要有下⾯这些。

    • > ⼤于运算符
    • < ⼩于运算符
    • >= ⼤于等于运算符
    • <= ⼩于等于运算符
    • == 相等运算符
    • != 不相等运算符

    eg:

    a == b;

    a != b;

    a < b;

    a > b;

    a = b;

    关系表达式通常返回 0 或 1 ,表示真伪。C 语⾔中, 0 表示伪,所有⾮零值表示真。⽐如, 20 > 12 返回 1 , 12 > 20 返回 0 。

    关系表达式常⽤于 if 或 while 结构。

    1. if (x == 3) {
    2. printf("x is 3.\n");
    3. }

    注意,相等运算符 == 与赋值运算符 = 是两个不⼀样的运算符,不要混淆。有时候,可能会写出下⾯的代码,它可以运⾏,但很容易出现意料之外的结果。

    if (x = 3) ...
    

    上⾯示例中,原意是 x == 3 ,但是不⼩⼼写成 x = 3 。这个式⼦表示对变量 x 赋值为 3 ,它的返回值为 3 , 所以 if 判断总是为真。

    为了防⽌出现这种错误,有的人喜欢将变量写在等号的右边。

    这样的话,如果把 == 误写成 = ,编译器就会报错。

    1. /* 报错 */
    2. if (3 = x) ...

    另⼀个需要避免的错误是,多个关系运算符不宜连⽤。

    i < j < k

    上⾯示例中,连续使⽤两个⼩于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果,即不是 保证变量 j 的值在 i 和 k 之间。因为表示运算符是从左到右计算,所以实际执⾏的是下⾯的表达式。

    (i < j) < k

    上⾯式⼦中, i < j 返回 0 或 1 ,所以最终是 0 或 1 与变量 k 进⾏⽐较。如果想要判断变量 j 的值是否 在 i 和 k 之间,应该使⽤下⾯的写法。

    i < j && j < k

    逻辑运算符

    逻辑运算符提供逻辑判断功能,⽤于构建更复杂的表达式,主要有下⾯三个运算符。

    • ! :否运算符(改变单个表达式的真伪)。
    • && :与运算符(两侧的表达式都为真,则为真,否则为伪)。
    • || :或运算符(两侧⾄少有⼀个表达式为真,则为真,否则为伪)。
    1. if (x < 10 && y > 20)
    2. printf("Doing something!\n");

    上⾯示例中,只有 x < 10 和 y > 20 同时为真, x < 10 && y > 20 才会为真。

    1. if (!(x < 12))
    2. printf("x is not less than 12\n");

    上⾯示例中,由于否运算符 ! 具有⽐ < 更⾼的优先级,所以必须使⽤括号,才能对表达式 x < 12 进⾏否运算,等价于 if (x >= 12) 。

    对于逻辑运算符来说,任何⾮零值都表示真,零值表示伪。⽐如, 5 || 0 会返回 1 , 5 && 0 会返回 0 。

    逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满⾜逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。

    if (number != 0 && 12/number == 2)

    上⾯示例中,如果 && 左侧的表达式( number != 0 )为伪,即 number 等于 0 时,右侧的表达式 ( 12/number == 2 )是不会执⾏的。因为这时左侧表达式返回 0 ,整个 && 表达式肯定为伪,就直接返回 0 ,不再执⾏右侧的表达式了。

    由于逻辑运算符的执⾏顺序是先左后右,所以下⾯的代码是有问题的。

    while ((x++ < 10) && (x + y < 20))

    上⾯示例中,执⾏左侧表达式后,变量 x 的值就已经变了。等到执⾏右侧表达式的时候,是⽤新的值在计算,这通常不是原始意图。

    位运算符

    C 语⾔提供⼀些位运算符,⽤来操作⼆进制位(bit)。

    (1)取反运算符~

    取反运算符 ~ 是⼀个⼀元运算符,⽤来将每⼀个⼆进制位变成相反值,即 0 变成 1 , 1 变成 0 。

    1. // 返回 01101100
    2. ~ 10010011

    上⾯示例中, ~ 对每个⼆进制位取反,就得到了⼀个新的值。 注意, ~ 运算符不会改变变量的值,只是返回⼀个新的值。

    (2)与运算符 &

    与运算符 & 将两个值的每⼀个⼆进制位进⾏⽐较,返回⼀个新的值。当两个⼆进制位都为 1 ,就返回 1 ,否则返回 0 。

    1. // 返回 00010001
    2. 10010011 & 00111101

    上⾯示例中,两个⼋位⼆进制数进⾏逐位⽐较,返回⼀个新的值。

    与运算符 & 可以与赋值运算符 = 结合,简写成 &= 。

    1. int val = 3;
    2. val = val & 0377;
    3. // 简写成
    4. val &= 0377;

    (3)或运算符 |

    或运算符 | 将两个值的每⼀个⼆进制位进⾏⽐较,返回⼀个新的值。两个⼆进制位只要有⼀个为 1 (包含两个都为 1 的情况),就返回 1 ,否则返回 0 。

    1. // 返回 10111111
    2. 10010011 | 00111101

    或运算符 | 可以与赋值运算符 = 结合,简写成 |= 。

    1. int val = 3;
    2. val = val | 0377;
    3. // 简写为
    4. val |= 0377;

    (4)异或运算符 ^

    异或运算符 ^ 将两个值的每⼀个⼆进制位进⾏⽐较,返回⼀个新的值。两个⼆进制位有且仅有⼀个为 1 ,就返回 1 ,否则返回 0 。

    1. // 返回 10101110
    2. 10010011 ^ 00111101

    异或运算符 ^ 可以与赋值运算符 = 结合,简写成 ^= 。

    1. int val = 3;
    2. val = val ^ 0377;
    3. // 简写为
    4. val ^= 0377;

    (5)左移运算符 <<

    左移运算符 << 将左侧运算数的每⼀位,向左移动指定的位数,尾部空出来的位置使⽤ 0 填充。

    1. // 1000101000
    2. 10001010 << 2

    上⾯示例中, 10001010 的每⼀个⼆进制位,都向左侧移动了两位。

    左移运算符相当于将运算数乘以2的指定次⽅,⽐如左移2位相当于乘以4(2的2次⽅)。

    左移运算符 << 可以与赋值运算符 = 结合,简写成 <<= 。

    1. int val = 1;
    2. val = val << 2;
    3. // 简写为
    4. val <<= 2;

    (6)右移运算符 >>

    右移运算符 >> 将左侧运算数的每⼀位,向右移动指定的位数,尾部⽆法容纳的值将丢弃,头部空出来的位置使⽤ 0 填充。

    1. // 返回 00100010
    2. 10001010 >> 2

    上⾯示例中, 10001010 的每⼀个⼆进制位,都向右移动两位。最低的两位 10 被丢弃,头部多出来的两位补 0 ,所以最后得到 00100010 。

    注意,右移运算符最好只⽤于⽆符号整数,不要⽤于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不⼀样的结果。

    右移运算符相当于将运算数除以2的指定次⽅,⽐如右移2位就相当于除以4(2的2次⽅)。 右移运算符 >> 可以与赋值运算符 = 结合,简写成 >>= 。

    1. int val = 1;
    2. val = val >> 2;
    3. // 简写为
    4. val >>= 2;

    逗号运算符

    逗号运算符⽤于将多个表达式写在⼀起,从左到右依次运⾏每个表达式。

    x = 10, y = 20;

    上⾯示例中,有两个表达式( x = 10 和 y = 20 ),逗号使得它们可以放在同⼀条语句⾥⾯。 逗号运算符返回最后⼀个表达式的值,作为整个语句的值。

    1. int x;
    2. x = 1, 2, 3;

    上⾯示例中,逗号的优先级低于赋值运算符,所以先执⾏赋值运算,再执⾏逗号运算,变量 x 等于 1 。

    运算优先级

    优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。

    3 + 4 * 5;

    上⾯示例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。

    如果两个运算符优先级相同,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合 (从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。

    5 * 6 / 2;
    

    上⾯示例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 ,再计算 6 / 2 。

    运算符的优先级顺序很复杂。下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列)。

    • 圆括号( () )
    • ⾃增运算符( ++ ),⾃减运算符( -- )
    •  ⼀元运算符( + 和 - )
    • 乘法( * ),除法( / )
    • 加法( + ),减法( - )
    • 关系运算符( < 、 > 等)
    • 赋值运算符( = )

    由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。

    int x = (3 + 4) * 5;

    上⾯示例中,由于添加了圆括号,加法会先于乘法进⾏运算。

    完全记住所有运算符的优先级没有必要,解决⽅法是多⽤圆括号,防⽌出现意料之外的情况,也有利于提⾼代码的可读性。


  • 相关阅读:
    MySql基础篇——存储过程和函数
    仿黑马点评-redis整合【三、缓存工具封装】
    vue项目推荐组件/工具库清单
    JVM 调优系列 1:“精通 JVM 调优,有过 JVM 调优经验”,简历敢写吗?薪资涨 5k 的技巧!
    PAT甲级--1083 List Grades 分数 25
    C++智能指针简介
    SD-WAN如何提升跨国企业网络性能?
    时间复杂度为 O(n^2) 的排序算法
    增材制造中如何使用HOOPS进行3D模型轻量化处理?
    C语言《数据结构与数据库/操作系统》实验测试数据集
  • 原文地址:https://blog.csdn.net/m0_54925305/article/details/125910241