• C语言第十二课(中):操作符详解【单目、关系、逻辑、条件操作符】


    目录

    前言:

    一、单目操作符!、-、+、&、sizeof、~、--、++、*、(类型):

            1.逻辑反操作!:

            2.正负值操作符-、+:

            3.取地址操作符 &与解引用操作符 *:

            ①.取地址操作符&:

            ②.解引用操作符 *:

            4.类型长度计算sizeof:

            5.按位取反~:

            6.前置与后置++、--操作符:

            7.强制类型转换(类型):

    二、关系操作符>、>=、<、<=、==、!=:

    三、逻辑操作符&&、||:

    四、条件操作符:

    五、总结:


    前言:

            前面我们对操作符进行了简单的分类:

    算术操作符移位操作符位操作符
    赋值操作符单目操作符关系操作符
    逻辑操作符条件操作符逗号表达式
    下标引用、函数调用和结构成员

            而在上一篇文章中我们学习了算数、移位、位与赋值操作符的相关知识。本文我将继续按照顺序,带领小伙伴们继续学习单目、关系、逻辑、条件这些我们在编写代码时常用的操作符。

    一、单目操作符!、-、+、&、sizeof、~、--、++、*、(类型):

            单目操作符,通俗的来讲就是只有一个操作数的操作符。是一类在进行操作时,只对一个操作数进行处理的操作符。

            1.逻辑反操作!:

            单目操作符' ! '表示逻辑反操作,即在逻辑层面表示“”、“”、“”等含义,例如:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int flag = 1;
    6. if (flag)
    7. {
    8. printf("FLAG\n");
    9. }
    10. if (!flag)
    11. {
    12. printf("!flag\n");
    13. }
    14. return 0;
    15. }

            编译运行查看输出打印结果:

            我们可以看到,在我们定义了一个值为1的变量flag后,我们使用了if分支语句进行了判断,判断的条件即为变量flag,此时flag的值为1判断为逻辑“真”执行打印操作将“ FLAG ”字样打印在了我们的屏幕上。

            接着我们又使用另一个if分支语句进行了判断,此时的判断条件变成了对flag进行逻辑反操作,也就是此时flag为1,判断为“真”,逻辑取反,为“假”,则不执行第二个if语句中的操作,不打印字样“ !flag ”。

            这样我们就知道了逻辑反操作符的作用方式了,那么它的作用会对值产生影响吗?我们一起来验证一下:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int flag1 = 5;
    6. int flag2 = !flag1;
    7. int flag3 = !flag2;
    8. printf("flag1 = %d\n", flag1);
    9. printf("flag2 = %d\n", flag2);
    10. printf("flag3 = %d\n", flag3);
    11. return 0;
    12. }

            我们首先定义了一个值为5的变量flag1,接着定义了一个值为对flag1逻辑取反操作的变量flag2,又定义了一个值为对flag2逻辑取反操作的变量flag3。即flag2、flag3为两个对flag1连续进行两次逻辑取反操作的变量,并将它们的值全部打印在屏幕上,我们把它编译运行看看结果:

            我们可以看到,该操作符在执行时是会对操作数的值造成影响的,当值为5时为真(在计算机的逻辑判断中,0为假,非0为真),进行逻辑取反后值变为0为假再次进行逻辑取反操作后不会变为原本的值,而是变为了逻辑“真”值1

            2.正负值操作符-、+:

            很多小伙伴们看到这里可能会有疑问了,上面不是说单目操作符指的是只有一个操作数的操作符吗?可是在执行加减操作时的操作符操作的是两个操作数呀?在这里小伙伴们应当注意,这里我们要学习的符号' - '和' + ',不是指算数运算中的加法和减法运算,而是用于表示数值政府的正负值操作符:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int flag1 = 5;
    6. int flag2 = -flag1;
    7. int flag3 = +flag1;
    8. int flag4 = +flag2;
    9. printf("flag2 = %d\n", flag2);
    10. printf("flag3 = %d\n", flag3);
    11. printf("flag4 = %d\n", flag4);
    12. return 0;
    13. }

            我们可以看到,通过使用这两个单目操作符,我们可以改变数据元素值的正负,同时我们也看到,在正数前面使用正号' + '时,数据元素的之不会发生变化 ,故正数前的' + '可以省略,同时,由于正号不会对数据元素的值造成影响,所以绝大多数情况下会被省略,在我们编写代码的过程中,除了用于标记值的正负供我们自己进行区分外,基本不会用到:

            3.取地址操作符 &与解引用操作符 *:

            ①.取地址操作符&:

            在我们的代码编写过程中,取地址操作符的使用可以说是随处可见了,在我们之前学习传址调用时,取地址操作符' & '也是很关键的一个操作符。而该符号的作用也很简单,它的作用就是读取出操作数在我们电脑内存中的存储地址。它的操作对象有很多种,其中就包括了常量变量数组等等:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. int arr[] = { 1,2,3,4,5 };
    7. printf("变量 a 的存储地址为:%p\n", &a);
    8. printf("数组arr的存储地址为:%p\n", arr);
    9. return 0;
    10. }

            我们通过编译器的内存查看窗口来验证我们取出的地址是否正确。

            首先我们来看我们的程序编译运行结果,打印出了变量与数组的存储地址:

            接着我们先来验证打印出的地址是否为内存中变量a的存储地址

            我们可以看到内存中变量a的存储地址的确为000000249EAFFB54

            然后我们来验证打印出的地址是否为内存中数组arr的存储地址

            可以看到内存中数组arr的存储地址也确实为000000249EAFFB78

            ②.解引用操作符 *:

            取地址操作符' & '也常常与解引用操作符' * '结合起来进行使用,使用它可以将我们使用取地址操作符取出的地址存储起来

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. printf("变量 a 在内存中的存储地址为:%p\n", &a);
    7. int* p = &a;
    8. //通过使用解引用操作符,可以将使用取地址操作符&取出的数据存储地址存储起来
    9. //我们定义的变量a为int类型,故用于存储其地址的p的类型应当对应为int*类型
    10. printf("变量 a 在内存中的存储地址为:%p\n", p);
    11. return 0;
    12. }

            编译运行后,我们看到在屏幕打印出的地址中,通过结合使用取地址操作符和解引用操作符能够成功存储我们取出的地址:

            并且在取得地址后,也可以通过使用解引用操作符,对该地址内的数据进行操作

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. int* p = &a;
    7. *p = 20;
    8. printf("变量 a 的值为:%d\n", a);
    9. return 0;
    10. }

            其实,取地址操作符,就像是通过一个工具(&),拿到了某人(某数据)家的门牌号(存储地址),并用另一个工具(*)把这个门牌号(存储地址)记录下来,并能够通过这个工具(*)记录的门牌号(存储地址)找到这个人(该数据)的家,并对这个人(该数据)家中的人(数据)进行操作:

            4.类型长度计算sizeof:

            各位小伙伴们在这里要注意了,sizeof 既是一个关键字,同时也是一个操作符,其功能是用于计算操作数的类型长度(以字节为单位)的。:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. printf("变量 a 的长度为:%d\n", sizeof(a));
    7. printf("类型int的长度为:%d\n", sizeof(int));
    8. return 0;
    9. }

            通过编译运行结果我们可以看到,这里打印出的并不是变量a的值,而是变量a的数据类型,即int类型的类型长度(以字节为单位)

            并且作为一个操作符,在使用时sizeof 后的括号在一定情况下可以省略,即sizeof后是变量名时可以省略,而当它后面是类型名时,括号不能省略,会直接报错:

            接着,我们要注意操作符sizeof内部的表达式不参与计算

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. short b = 20;
    7. printf("%d", sizeof(b = a + 10));
    8. printf("变量 b 的值为:%d\n", b);
    9. return 0;
    10. }

            我们看到,sizeof操作符后括号内的计算并没有执行,仍为原值:

            并且我们通过观察也发现,虽然变量a与变量b的数据类型不同,但操作符sizeof所计算的仍然是括号内更靠前的变量b的数据类型长度。原因是其中的变量a为整形,在存储时将会在内存中开辟四个字节的空间,而变量b为short类型,在内存中仅占两个字节,若想要将四个字节的数据放进两个字节的空间中,将会发生截断只放入两个字节的数据还有两个字节的数据将不会被放入

            5.按位取反~:

            按位取反操作符~很好理解,我们前面学习过,数据在内存中存储时,存储的是其所对应的二进制补码,而~操作符,就是将操作数的二进制补码的每一位都进行取反操作,这里的每一位也包括符号位在内,没有例外:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 0;
    6. int b = ~a;
    7. printf("变量 a 的值为:%d\n", a);
    8. printf("变量 b 的值为:%d\n", b);
    9. return 0;
    10. }

            我们都知道变量a,即0的二进制补码为:

    0的二进制补码:0000 0000 0000 0000 0000 0000 0000 0000

            对其进行按位取反操作,即b的二进制补码则为:

    b的二进制补码:1111 1111 1111 1111 1111 1111 1111 1111

            而二进制补码全为1的数值为-1,即b的值为-1。编译运行验证结果,我们可以得出,按位取反操作包括符号位在内,在每一位上均进行取反操作

            6.前置与后置++、--操作符:

            ++和--操作符很常用也很简单,++表示将值+1,--表示将值-1

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. printf("初始变量 a 的值为:%d\n", a);
    7. a++;
    8. printf("++后变量 a 的值为:%d\n", a);
    9. a--;
    10. printf("--后变量 a 的值为:%d\n", a);
    11. return 0;
    12. }

            我们在这里初始化变量a的值为1,然后对其进行++,使得变量a的值进行了加一操作变为2,接着又对其进行--变量a的值便又进行了减一操作变为1:

            而关于++和--操作,我们需要注意的是忙着两个操作符在前置与后置时所起到的作用是不同的

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. printf("变量 a 的值为: % d\n", a);
    7. printf("\n");
    8. int b = a++;
    9. printf("后置++ b 的值为: % d\n", b);
    10. printf("变量 a 的值为: % d\n", a);
    11. printf("\n");
    12. a = 1;
    13. //重置变量a
    14. int c = ++a;
    15. printf("前置++ c 的值为: % d\n", c);
    16. printf("变量 a 的值为: % d\n", a);
    17. printf("\n");
    18. a = 1;
    19. //重置变量a
    20. int d = a--;
    21. printf("后置-- d 的值为: % d\n", d);
    22. printf("变量 a 的值为: % d\n", a);
    23. printf("\n");
    24. a = 1;
    25. //重置变量a
    26. int e = --a;
    27. printf("前置-- e 的值为: % d\n", e);
    28. printf("变量 a 的值为: % d\n", a);
    29. return 0;
    30. }

            通过观察,我们可以得出结论:前置时先操作再赋值,后置时先赋值再操作

            即当后置++时,先将a的值1赋值给b,再将变量a进行了加一操作后变为2,此时变量b的值为1而变量a的值为2;当前置++时,先将变量a进行加一操作变为2,再将2赋给变量b,此时变量a与变量b的值均为2

            同理,当后置--时,先将a的值1赋值给b,再将变量a进行了加一操作后变为0,此时变量b的值为1而变量a的值为0;当前置--时,先将变量a进行减一操作变为0,再将0赋给变量b,此时变量a与变量b的值均为0

            7.强制类型转换(类型)

            在我们编写代码时,在一个工程项目中往往会用到很多种不同的数据类型,而不同的数据类型在进行赋值等操作时,就有可能会出现错误:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. float b = 3.14;
    7. a = b;
    8. printf("变量 a 的值为:%d\n", a);
    9. return 0;
    10. }

            我们可以看到,变量a的数据类型为整型变量int,而变量b的数据类型为浮点型float,在进行赋值时因为数据类型不同,导致了赋值操作的失败:

            所以在这种情况下想要进行处理时,就应当对数据类型进行强制转换

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. float b = 3.14;
    7. a = (int)b;
    8. printf("变量 a 的值为:%d\n", a);
    9. return 0;
    10. }

            在这种情况下强行进行赋值时应当将float类型的变量b强制转换为int类型后再进行赋值。而我们在编写代码时应当注意尽可能避免强制类型转换的使用,因为进行强制类型转换时虽然可以去除系统警告,但是有可能会导致部分数据的丢失。故不到万不得已尽量不要去使用

    二、关系操作符>、>=、<、<=、==、!=:

            关系操作符的使用很简单,基本没有什么可讲的,但我们在使用时仍需要注意一些小的陷阱,例如要注意不要将赋值操作符' = '与关系操作符' == '搞混

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. int b = 2;
    7. int c = 2;
    8. int d = 1;
    9. int e = 1;
    10. int f = 1;
    11. if (b > a)
    12. {
    13. if (c >= b)
    14. {
    15. if (d < c)
    16. {
    17. if (e <= d)
    18. {
    19. if (f == e)
    20. //注意区分赋值操作符=与关系操作符==
    21. {
    22. printf("NICE!!!\n");
    23. }
    24. }
    25. }
    26. }
    27. }
    28. return 0;
    29. }

    三、逻辑操作符&&、||:

            逻辑操作与操作符' && '与逻辑操作或操作符' || '同样使用简单,均表示逻辑层面的“与”和“或”。逻辑与操作符' && '表示其两边的表达式均为真时为真,否则为假; 逻辑或操作符' || '表示当其两边的表达式有任一表达式为真时为真,均为假时为假。在使用时同样需要注意区分按位与操作符' & '与逻辑与操作符' && '按位或操作符' | '与逻辑或操作符' || '

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. int b = 0;
    7. if (a || b)
    8. {
    9. if (a && b)
    10. {
    11. printf("HEHE\n");
    12. }
    13. else
    14. {
    15. printf("LALA\n");
    16. }
    17. }
    18. return 0;
    19. }

    四、条件操作符:

            条件操作符也叫做三目操作符,原因是其操作数最多可以有三个

    exp1 ? exp2 : exp3

            他表示,对表达式exp1进行判断,若判断为真则执行表达式exp2,若判断为假则执行表达式exp3。例如:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 5;
    6. int b = 0;
    7. if (a > 5)
    8. {
    9. b = 3;
    10. }
    11. else
    12. {
    13. b = -3;
    14. }
    15. printf("变量 b 的值为:%d\n", b);
    16. return 0;
    17. }

            上述代码的编译运行结果为:

            该代码可以通过使用条件操作符简化为:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 5;
    6. int b = 0;
    7. b = a > 5 ? 3 : -3;
    8. printf("变量 b 的值为:%d\n", b);
    9. return 0;
    10. }

            编译运行可以得到同样的结果,并且采用这样的形式,我们的代码直接减少了七行,大幅提高了我们代码的空间效率,可以使我们的代码在完成目标功能的前提下使用更少的空间

    五、总结:

            至此,我们今天关于单目、关系、逻辑和条件操作符的介绍就到此为止了,这些操作符应用极其广泛,是我们编写代码过程中的好帮手,希望各位下伙伴们下去以后多多思考和理解,勤加练习,熟练掌握相关的原理和用法,提高自己的代码编写工作效率。

             希望各位小伙伴们能从今天的介绍中获得一些收获和启发,我也十分荣幸能为各位小伙伴们的学习生活提供一些帮助。彗星般的人生可以短暂,但绝不黯淡或沉沦!

            新人初来乍到,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位看官老爷随时私信批评指正!

  • 相关阅读:
    How to customize the text of a generic Qlik Sense error message
    YOLO目标检测——烟雾检测数据集下载分享【含对应voc、coco和yolo三种格式标签】
    springboot疫情防控学生自助申报系统毕业设计源码260839
    2023 极术通讯-安谋科技发布“山海”S20F安全解决方案,持续加码智能汽车“芯”赛道
    交通流预测——day59 交通网络动态性与多权重交通图卷积(MW-TGC)网络的交通预测
    【小黑送书—第七期】>>程序员To架构师?一起来看架构师系列书籍推荐
    @PostConstruct注解
    每天一个数据分析题(一百八十三)
    ChatGPT私有数据结合有什么效果?它难吗?
    mysql优化---如何搭建mysql的主从关系和mycat中间件
  • 原文地址:https://blog.csdn.net/weixin_59392935/article/details/128077329