• C语言-操作符详解(5)


    目录

    1. 操作符分类:

    思维导图:

    2. 算术操作符

    3. 移位操作符

    3.1 左移操作符

    3.2 右移操作符

    4. 位操作符

    4.1 &

    4.2 |

    4.3 ^

    5. 赋值操作符

    6. 单目操作符

    6.1 单目操作符介绍

    6.2 sizeof 和数组

    7. 关系操作符 

    8. 逻辑操作符

    9. 条件操作符

    10. 逗号表达式

    11. 结构成员

    12. 表达式求值

    12.1 隐式类型转换

    12.2 算术转换

    12.3 操作符的属性

    写在最后


    1. 操作符分类:

    思维导图

    2. 算术操作符

    +     -     *     /     %
    1. #include
    2. int main()
    3. {
    4. int a = 10 / 3;//除法操作符,两边都是整数就执行整数除法
    5. printf("%d\n", a);
    6. double b = 10.0 / 3;//两边只要有一个是浮点数就能计算出小数
    7. printf("%lf\n", b);
    8. printf("%.1lf\n", b);//在lf前面加.1能保留小数点后一位,.几就保留几位
    9. return 0;
    10. }

    输出结果:

    1. 输出:
    2. 3
    3. 3.333333
    4. 3.3
    1. #include
    2. int main()
    3. {
    4. int ret = 10 % 3;//取模,操作符两边必须是整数
    5. printf("%d\n,ret");
    6. return 0;
    7. }

    输出结果:

    输出:1
    

    3. 移位操作符

    在讲移位前,需要知道一些基本的概念:

    二进制:

    二进制整数有三种表示形式:

    1. 原码

    2. 反码

    3. 补码

    而在内存中存储的是:二进制的补码

    所以参数在移位时是二进的补码。

    例:

    1. int main()
    2. {
    3. //整形类型占四个字节(32个比特位),二进制的表现形式:
    4. int a = 10;
    5. //00000000000000000000000000001010 - 原码
    6. //00000000000000000000000000001010 - 反码
    7. //00000000000000000000000000001010 - 补码
    8. int a = -10;
    9. //10000000000000000000000000001010 - 原码
    10. //11111111111111111111111111110101 - 反码
    11. //11111111111111111111111111110110 - 补码
    12. return 0;
    13. }

    通过观察发现正数原码、反码、补码相同,

    负数反码是:原码符号位不变,其它位按位取反,补码是:反码+1。

    3.1 左移操作符

    例:

    1. #include
    2. int main()
    3. {
    4. //左移操作符:<<
    5. //规则:左边抛弃,右边补零
    6. int a = 10;
    7. //a:
    8. //00000000000000000000000000001010 - 补码
    9. int b = a << 1;
    10. //b:
    11. //00000000000000000000000000010100 - 补码
    12. printf("%d\n", b);
    13. int c = -10;
    14. //c:
    15. //10000000000000000000000000001010 - 原码
    16. //11111111111111111111111111110101 - 反码
    17. //11111111111111111111111111110110 - 补码
    18. int d = c << 1;
    19. //d:
    20. //11111111111111111111111111101100 - 补码
    21. //11111111111111111111111111101011 - 反码
    22. //10000000000000000000000000010100 - 原码
    23. printf("%d\n", d);
    24. return 0;
    25. }

    (注:printf 打印出来给我们看的是原码。)

    输出结果:

    1. 输出:
    2. 20
    3. -20

    总结:

    左移操作也可以看成是乘二的操作。

    3.2 右移操作符

    例:

    1. #include
    2. int main()
    3. {
    4. //右移操作符:>>
    5. //1.算数右移:左边补符号位,右边抛弃(常用)
    6. //2.逻辑右移:左边补零,右边抛弃
    7. int a = 10;
    8. //a:
    9. //00000000000000000000000000001010 - 补码
    10. int b = a >> 1;
    11. //b:
    12. //00000000000000000000000000000101 - 补码
    13. printf("%d\n", b);
    14. int c = -10;
    15. //c:
    16. //10000000000000000000000000001010 - 原码
    17. //11111111111111111111111111110101 - 反码
    18. //11111111111111111111111111110110 - 补码
    19. int d = c >> 1;
    20. //d:
    21. //11111111111111111111111111111011 - 补码
    22. //11111111111111111111111111111010 - 反码
    23. //10000000000000000000000000000101 - 原码
    24. printf("%d\n", d);
    25. return 0;
    26. }

    输出结果:

    1. 输出:
    2. 5
    3. -5

    注:无论是右移还是左移的位数都不能为负数。

    例:

    1. ret>>-1
    2. 这样写是错误的

    4. 位操作符

    &   按位与

     |     按位或 

    ^    按位异或

    4.1 &

    例:

    1. #include
    2. int main()
    3. {
    4. //& - 按二进制与
    5. //规则:有零则零
    6. int a = 3;
    7. //00000000000000000000000000000011 - 补码
    8. int b = -5;
    9. //10000000000000000000000000000101 - 原码
    10. //11111111111111111111111111111010 - 反码
    11. //11111111111111111111111111111011 - 补码
    12. int c = a & b;
    13. //00000000000000000000000000000011 - a 补码
    14. //11111111111111111111111111111011 - b 补码
    15. //00000000000000000000000000000011 - c 原码
    16. printf("%d\n", c);
    17. return 0;
    18. }

    输出结果:

    输出:3

    4.2 |

    例:

    1. #include
    2. int main()
    3. {
    4. // | - 按二进制位或
    5. // 规则:有一则一
    6. int a = 3;
    7. //00000000000000000000000000000011 - 补码
    8. int b = -5;
    9. //10000000000000000000000000000101 - 原码
    10. //11111111111111111111111111111010 - 反码
    11. //11111111111111111111111111111011 - 补码
    12. int c = a | b;
    13. //00000000000000000000000000000011 - a补码
    14. //11111111111111111111111111111011 - b补码
    15. //11111111111111111111111111111011 - c补码
    16. //11111111111111111111111111111010 - c反码
    17. //10000000000000000000000000000101 - c原码
    18. printf("%d\n", c);
    19. return 0;
    20. }

    输出结果:

    输出:-5

    4.3 ^

    例:

    1. #include
    2. int main()
    3. {
    4. // ^ - 按二进制位异或
    5. // 规则:相同为零,相异为一
    6. int a = 3;
    7. //00000000000000000000000000000011 - 补码
    8. int b = -5;
    9. //10000000000000000000000000000101 - 原码
    10. //11111111111111111111111111111010 - 反码
    11. //11111111111111111111111111111011 - 补码
    12. int c = a ^ b;
    13. //00000000000000000000000000000011 - a补码
    14. //11111111111111111111111111111011 - b补码
    15. //11111111111111111111111111111000 - c补码
    16. //11111111111111111111111111110111 - c反码
    17. //10000000000000000000000000001000 - c原码
    18. printf("%d\n", c);
    19. return 0;
    20. }

    输出结果:

    输出:-8

    练习:

    一道编程题:

    如何不创建临时变量完成两个数的交换

    例:

    1. #include
    2. int main()
    3. {
    4. int a = 3;
    5. int b = 5;
    6. printf("%d %d\n", a, b);
    7. a = a ^ b;
    8. b = a ^ b;// b = a ^ b ^ b // 而 a ^ b ^ b = a 所以b就赋值为a
    9. a = a ^ b;// a = a ^ b ^ a // 而 a ^ b ^ a = b 所以a就赋值为b
    10. printf("%d %d\n", a, b);
    11. return 0;
    12. }

    输出结果:

    1. 输出:
    2. 3 5
    3. 5 3

    用异或操作符交换两个变量的弊端:

    1. 可读性差

    2. 效率没有创建临时变量高

    3. 异或只能用于整数变量的交换

    总结:这种方法了解即可,平时使用临时变量交换两个变量的方法更好。

    5. 赋值操作符

    赋值操作符能给变量赋值。

    例:

    1. int main()
    2. {
    3. int a = 10;
    4. a = 100;// = 能将a赋值成100
    5. return 0;
    6. }

    还有复合赋值符:

    例:

    +=     

    -=       

    *=       

    /=       

    %=

    >>=     

    <<=     

    &=      

    |=     

    ^=

    这些运算符都可以写成复合的效果(规则都是一样的)

    例:

    1. int main()
    2. {
    3. int a = 10;
    4. a += 10;//这个其实就是:a = a + 10
    5. printf("%d\n", a);//输出的结果就是20
    6. return 0;
    7. }

    6. 单目操作符

    !              逻辑反操作

    -              负值

    +             正值

    &             取地址

    sizeof      操作数的类型长度(以字节为单位)

    ~             对一个数的二进制按位取反

    --            前置、后置--

    +            前置、后置++

    *             间接访问操作符(解引用操作符)

    (类型)     强制类型转换

    6.1 单目操作符介绍

    例1:

    1. #include
    2. int main()
    3. {
    4. //! 逻辑反操作
    5. //C语言中0表示假,非零表示真
    6. int n = 0;
    7. if (n)
    8. {
    9. printf("1\n");
    10. }
    11. if (!n)//!逻辑反操作,将假变为真(也能从真变假)
    12. {
    13. printf("2\n");
    14. }
    15. return 0;
    16. }

    输出结果:

    输出:2

    例2:

    1. #include
    2. int main()
    3. { // + -
    4. int a = -10;
    5. printf("%d\n", a);
    6. printf("%d\n", -a);
    7. printf("%d\n", +a);//‘+’几乎没有用处
    8. return 0;
    9. }

    输出结果:

    1. 输出:
    2. -10
    3. 10
    4. -10

    例3:

    1. #include
    2. int main()
    3. {
    4. // ~ 按位取反
    5. //00000000000000000000000000000000
    6. //11111111111111111111111111111111 - 补码是全1
    7. int a = 0;
    8. printf("%d\n", ~a);
    9. }

    输出结果:

    输出:-1

    例4:

    1. #include
    2. int main()
    3. {
    4. //++
    5. int a = 10;
    6. int b = a++;//后置++,先使用,再++
    7. printf("%d\n", b);
    8. printf("%d\n", a);
    9. int c = 10;
    10. int d = ++c;//前置++,先++,再使用
    11. printf("%d\n", d);
    12. printf("%d\n", c);
    13. return 0;
    14. }

    输出结果:

    1. 输出:
    2. 10
    3. 11
    4. 11
    5. 11

    注:-- 的规则与 ++ 是一样的。

    1. #include
    2. int main()
    3. {
    4. // ++ -- 是带有副作用的
    5. // 会改变变量自身的值
    6. //1
    7. int a = 10;
    8. int b = ++a;//b=11 a=11
    9. //2
    10. int a = 10;
    11. int b = a + 1;//b=11 a=10
    12. return 0;
    13. }

    例5:

    1. #include
    2. int main()
    3. { // (类型)强制类型转换
    4. int a = (int)3.14;//这样编译器就不会报警告
    5. srand((unsigned int)time(NULL));//将类型为time_t的time转成srand需要的无符号整形
    6. return 0;
    7. }

    6.2 sizeof 和数组

    例:

    1. #include
    2. void test1(int arr[])//本质上传过来的是数组首元素的地址
    3. {
    4. printf("%d\n", sizeof(arr));//地址在32位环境占4个字节
    5. } //在64位的环境中占8个字节
    6. void test2(char ch[])
    7. {
    8. printf("%d\n", sizeof(ch));//我是32位的环境,所以输出4
    9. }
    10. int main()
    11. {
    12. int arr[10] = { 0 };
    13. char ch[10] = { 0 };
    14. printf("%d\n", sizeof(arr));//int类型占4个字节
    15. printf("%d\n", sizeof(ch));//char类型占1个字节
    16. test1(arr);
    17. test2(ch);
    18. return 0;
    19. }

    输出结果:

    1. 输出:
    2. 40
    3. 10
    4. 4
    5. 4

    7. 关系操作符 

    >

    >=

    <

    <=

    !=       用于测试“不相等”

    ==      用于测试“相等”

    注:别把 “==” 和 “=” 给写错了。

    8. 逻辑操作符

    &&     逻辑与  ( a&&b    a,b都要满足才真)

     ||       逻辑或  (   a||b     a,b只要满足一个就真)

    例:

    1. #include
    2. int main()
    3. {
    4. int i = 0, a = 0, b = 2, c = 3, d = 4;
    5. i = a++ && ++b && d++;
    6. //因为a=0,++的优先级较低,所以(a++)这个表达式值是0,而&&有零那就是零了,
    7. //所以(++b)(d++)都不会发生
    8. printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
    9. return 0;
    10. }

    输出结果:

    1. 输出:
    2. a = 1
    3. b = 2
    4. c = 3
    5. d = 4
    1. #include
    2. int main()
    3. {
    4. int i = 0, a = 1, b = 2, c = 3, d = 4;
    5. i = a++ || ++b || d++;
    6. //(a++)这个表达式为真,那||左边就无须计算了
    7. //所以(++b)(d++)都不计算了
    8. printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
    9. return 0;
    10. }

    输出结果:

    1. 输出:
    2. a = 2
    3. b = 2
    4. c = 3
    5. d = 4

    9. 条件操作符

    exp1 ? exp2 : exp3

    例:

    这是一个求较大值的代码:

    1. #include
    2. int max(int a, int b)
    3. {
    4. if (a > b)
    5. return a;
    6. else
    7. return b;
    8. }
    9. int main()
    10. {
    11. int a = 10;
    12. int b = 20;
    13. printf("%d\n", max(a, b));
    14. return 0;
    15. }

    输出结果:

    输出:20

    而用条件操作符:

    1. #include
    2. int max(int a, int b)
    3. {
    4. return (a > b ? a : b);//如果a>b则取a,否则取b
    5. }
    6. int main()
    7. {
    8. int a = 10;
    9. int b = 20;
    10. printf("%d\n", max(a, b));
    11. return 0;
    12. }

    输出结果:

    输出:20

    两种写法是一模一样的。

    10. 逗号表达式

    逗号表达式:

    1. 用逗号隔开的多个表达式。

    2. 从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

    例:

    1. #include
    2. int main()
    3. {
    4. int a = 1;
    5. int b = 2;
    6. int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
    7. //先算a = b + 10,算出a = 12,再算b = a + 1,算出b = 13,
    8. //最后结果就是最后一个表达式(b = a + 1)的值
    9. printf("c = %d\n", c);
    10. return 0;
    11. }

    输出结果:

    输出:13

    11. 结构成员

    结构体在C语言中一般用于描述复杂对象,比如人、书,C语言中没有这样的类型,

    但是结构体能让我们创建新的类型。

    12. 表达式求值

    12.1 隐式类型转换

    CPU在进行运算的时候一般使用整形int,所以在有些时候,当一个小于整形的类型进行计算时,计算机就会先进行整形提升再进行运算,这就是隐式类型转换。

    (通用CPU是难以直接实现两个非整形的直接相加运算)

    例:

    1. // char short int long ...
    2. // 1 2 4
    3. int main()
    4. {
    5. //char --> signed char
    6. char a = 3;
    7. //截断
    8. //00000000000000000000000000000011
    9. //00000011 - a
    10. //
    11. char b = 127;
    12. //00000000000000000000000001111111
    13. //01111111 - b
    14. char c = a + b;
    15. //00000011
    16. //01111111
    17. //整型提升
    18. //00000000000000000000000000000011 - a
    19. //00000000000000000000000001111111 - b
    20. //00000000000000000000000010000010 - a + b =c
    21. //截断
    22. //10000010 - c
    23. printf("%d\n", c);
    24. //%d 是打印十进制的整数
    25. //11111111111111111111111110000010 - 补码
    26. //11111111111111111111111110000001 - 反码
    27. //10000000000000000000000001111110 - 原码
    28. //-126
    29. return 0;
    30. }

    输出结果:

    输出:-126

    注:

    char:

    有符号的char的取值范围是:-128~127

    无符号的char的取值范围是:0~255

    12.2 算术转换

    不同操作类型的数进行运算时会进行算数转换:

    long double

    double

    float unsigned long int

    long int

    unsigned int

    int

    注:算数转换不合理会产生一些问题:

    1. #include
    2. int main()
    3. {
    4. float f = 3.14;
    5. int num = f;//隐式转换,会有精度丢失
    6. printf("%d\n", num);
    7. return 0;
    8. }

    输出结果:

    输出:3

    12.3 操作符的属性

    复杂表达式的求值有三个影响的因素:

    1. 操作符的优先级

    2. 操作符的结合性

    3. 是否控制求值顺序

    但是,我们写出的代码如果没有唯一的运算路径,就会出问题

    所以,总结:平时写代码的运算路径要唯一。

    写在最后

    以上就是本篇文章的内容了,感谢你的阅读。

    如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

    如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

    之后我还会输出更多高质量内容,欢迎收看。

  • 相关阅读:
    数据库面试题之Mysql
    netstat 及 ifconfig 是如何工作的。
    服务器如何开启远程连接?
    算法-动态规划/中心扩散法-最长回文子串
    【高等数学】弧微分、渐近线、曲率和曲率半径
    C++ Reference: Standard C++ Library reference: C Library: cwchar: wcstoull
    【Unity HDRP渲染管线下的WorleyUtilities文件,“Hash”函数】
    MTK OEM解锁步骤
    神经网络硕士就业前景,神经网络就业怎么样
    计算机网络网络层
  • 原文地址:https://blog.csdn.net/Locky136/article/details/127812345