• 操作符精讲——这些操作符你还记得几个?


    前言

    Hello各位老铁们,还记得我在 初识C语言(下) 中介绍的操作符吗?在初识部分,我向大家罗列了C语言中所有的操作符,并且选择性的讲解了一些常用操作符。本章在此基础之上,继续深入学习和使用操作符!

    一、操作符分类

    下面是C语言中所有的操作符种类:

    1. 算术操作符
    2. 移位操作符
    3. 位操作符
    4. 赋值操作符
    5. 单目操作符
    6. 关系操作符
    7. 逻辑操作符
    8. 条件操作符
    9. 逗号表达式
    10. 下标引用、函数调用和结构成员

    初识C语言(下)中,小编已经对部分操作符进行了详细介绍,这里就不在赘述。下面就对余下操作符进行讲解:

    二、操作符精讲

    🍑1、原码、反码、补码

    在介绍移位操作符和位操作符之前,我们有必要了解一下有符号整数在计算机中的存储方式:

    1. 首先整数在计算机中以2进制表示,其中有符号整形的2进制又包含原码、反码以及补码。
    2. 规定有符号整数的最高位表示符号位,0表示正数,1表示负数。其它位为数值位。
    3. 正整数的原码、反码、补码相同。
    4. 负整数的原码:即直接将数值翻译为2进制位+符号位。
    5. 负整数的反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
    6. 负整数的补码:反码+1就得到补码。
    7. 整数在内存实际存放的是补码,而我们使用的是原码。

    📝例如:
    正整数的原、反、补相同:

    负整数的原反补需要转换:
    在这里插入图片描述

    🍑2、移位操作符

    << 左移操作符
    >> 右移操作符
    注:移位操作符的操作数只能是整数。

    (1)左移操作符

    移位规则:左边抛弃、右边补0
    📝例如:以4和-4为例

    注意:a<<1,当a的未被赋值的情况下,自身的值不会发生变化。右移同理

    代码展示:

    #include
    int main()
    {
    	int a = 4;
    	//00000000000000000000000000000100 - 4的补码
    	int b = a << 1;//把a向左移动一位
    	//00000000000000000000000000001000 - 8的补码
    	printf("a=%d b=%d\n", a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    #include
    int main()
    {
    	int a = -4;
    	//10000000000000000000000000000100 - -4的原码
    	//11111111111111111111111111111011 - -4的反码
    	//11111111111111111111111111111100 - -4的补码
    
    	int b = a << 1;//把a向左移动一位
    	//11111111111111111111111111111000 - b中存储的补码
    	//11111111111111111111111111110111 - b的反码
    	//10000000000000000000000000001000 - b的原码
    	//-8
    	printf("a=%d b=%d\n", a, b);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    拓展:左移是把2进制向高位移动,每移动1位有扩大2倍的效果

    (2)右移操作符

    移位规则:

    1. 逻辑移位 左边用0填充,右边丢弃
    2. 算术移位 左边用原该值的符号位填充,右边丢弃

    在C语言中,C语言标准并没有规定有符号数应该使用那种类型的右移,而是取决于编译器。但是绝大多数编译器都采用算术右移。对于无符号数,自然采用逻辑右移。

    📝例如:同样以4和-4为例

    #include
    int main()
    {
    	int a = 4;
    	//00000000000000000000000000000100 补码
    	int b = a >> 1;
    	//00000000000000000000000000000010 补码
    	printf("a=%d b=%d\n", a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    #include
    int main()
    {
    	int a = -4;
    	//10000000000000000000000000000100 - -4的原码
    	//11111111111111111111111111111011 - -4的反码
    	//11111111111111111111111111111100 - -4的补码
    
    	int b = a >> 1;
    	//11111111111111111111111111111100
    	//11111111111111111111111111111110 - b在内存中的补码
    	//11111111111111111111111111111101 - b的反码
    	//10000000000000000000000000000010 - b的原码
    	//-2
    	printf("a=%d b=%d\n", a, b);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    拓展:右移是把2进制向低位移动,每移动1位有缩小2倍的效果

    🍑3、位操作符

    & 按位与
    | 按位或
    ^ 按位异或
    ~取反运算符
    注:他们的操作数必须是整数。

    (1) & 按位与操作符

    规则: 有0则为0,两个同时为1才为1

    (2) | 按位或操作符

    规则: 有1则为1,两个同时为0才为0
    在这里插入图片描述

    (3) ^ 按位异或操作符

    规则: 相同为0,相异为1

    (4) ~ 按位取反操作符

    规则: 0为1,1为0

    📝一道变态的面试题:
    不能创建临时变量(第三个变量),实现两个数的交换。
    方法一:

    int main()
    {
    	int a = 3;
    	int b = 5;
    
    	a = a + b;//将(a+b)的和存到a中
    	b = a - b;//a的值为(a+b)的和-b,由于a=a+b即b=(a=a-b)
    	a = a - b;//b的值为(a+b)的和-a,由于a=a+b且b=a,即a=(b=a-b)
    	printf("a=%d b=%d\n", a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    缺陷:两个整数加和可能越界
    方法二:

    //思路:
    //a^a=0
    //0^a=a
    #include 
    int main()
    {
    	int a = 10;
    	int b = 20;
    	a = a ^ b;
    	b = a ^ b;//b=a ^ b ^ b
    	a = a ^ b;//a=a^b^a
    	printf("a = %d b = %d\n", a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这道题在交换变量内容时一反常态,解题思路比较绕,建议大家反复观看,重在理解。

    🍑4、sizeof详解

    (1) sizeof+变量或类型

    sizeof操作符的作用是:返回操作数的类型长度(以字节为单位)
    注意:sizeof的操作数是变量名时可以不带括号,如果是类型名必须带括号

    int main()
    {
    	int a = 10;
    	int* p;
    	int arr[10];
        //%zu专门打印sizeof的返回值
    	printf("%zu\n", sizeof(a));//int  4
    	printf("%zu\n", sizeof a);//int  4
    	printf("%zu\n", sizeof(int));//int  4
    	//printf("%zu\n", sizeof int);//err
    	
    	printf("%zu\n", sizeof(p));//int* 4
    	printf("%zu\n", sizeof(arr));//(int [10])40去掉数组名剩下的就是数组类型 
    	printf("%zu\n", sizeof(arr[10]));//int 4
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (2) sizeof和数组

    #include 
    void test1(int arr[])
    {
    	printf("%d\n", sizeof(arr));
    }
    void test2(char ch[])
    {
    	printf("%d\n", sizeof(ch));
    }
    int main()
    {
    	int arr[10] = { 0 };
    	char ch[10] = { 0 };
    	printf("%d\n", sizeof(arr));//sizeof 数组名,计算的是整个数组的大小//40
    	printf("%d\n", sizeof(ch)); //10
    	test1(arr);//数组传参传递的是首元素的地址//8或4(取决于32位还是64位)
    	test2(ch);//数组传参传递的是首元素的地址 //8或4(取决于32位还是64位)
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (3) sizeof和表达式

    注意:sizeof里面的表达式不参与计算

    #include
    int main()
    {
    	short s = 10;
    	int a = 2;
    	s = a + 5;
    	//sizeof返回的是类型的大小,而表达式s=a+5返回的是s的类型。即short类型
    	//sizeof里面的表达式不参与计算
    	printf("%d\n", sizeof(s = a + 5));//2
    	printf("%d\n", s);//7
    
    	return 0;
    }
    //输出:2,7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    🍑5、逻辑操作符

    && 逻辑与
    || 逻辑或
    ! 逻辑非

    (1) 操作符&&与||

    &&与||使用和理解还是比较简单的,比如判断闰年:

    #include
    int main()
    {
    	int year = 2000;
    	if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
    	{
    		printf("是闰年");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    补充:&&与||在表达式计算时是会控制求值顺序的。例如下面一道360的笔试题:

    #include 
    int main()
    {
        int i = 0,a=0,b=2,c =3,d=4;
        i = a++ && ++b && d++;
        //i = a++||++b||d++;
        printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
        return 0; }
    //程序输出的结果是什么?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当i=a++&&++b&&d++时,由于a=0即为假,对于&&操作符,一旦有一个表达式为假,后面的表达式就不在计算,判定整体为假。所以只计算a++,求得:a=1,b=2,c=3,d=4

    当i=a++||++b||d++时,a=0为假,++b=3为真,所以a++||++b整体为真,对于||操作符,有真则为真,所以表达式d++不在计算,求得:a=1,b=3,c=3,d=4

    (2) 逻辑非:

    “逻辑非”就是指本来值的反值。
    例如:" !0" 这个逻辑表达式的值为1.
    " !1" 这个逻辑表达式的值为0.

    🍑6、逗号表达式

    exp1, exp2, exp3, …expN
    逗号表达式,就是用逗号隔开的多个表达式。
    逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

    📝例如:

    //代码1
    int a = 1;
    int b = 2;
    int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
    //可求得:(a>b)==0,a=12,a=12,b=13,即c=13
    
    //代码2
    //用于判断
    if (a =b + 1, c=a / 2, d > 0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    逗号表达式的其它用法:
    逗号表达式在一定情境下还可以简化代码。

         ...
    count_val(a);
    while (a > 0) 
    {
    	//业务处理
    	a = get_val();
    	count_val(a);
    }
    //如果使用逗号表达式,改写:
    while (a = get_val(), count_val(a), a > 0) {
    	//业务处理
    }
    //是不是简洁多了?
         ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    总结

    本章重点介绍了原、反、补码,移位操作符、位操作符、sizeof、逻辑操作符以及逗号表达式。最后希望看完本文能够对您有所帮助,如有疑问欢迎随时交流!铁汁们,我们下期再见!
    下期预告:表达式求值——整形提升与算数转换

    在这里插入图片描述

  • 相关阅读:
    电子行业库存管理痛点与WMS仓储管理系统解决方案
    Docker - WEB应用实例
    lvs集群
    23 - 如何优化JVM内存分配?
    【Java集合类面试二十】、请介绍LinkedHashMap的底层原理
    安装clang
    A-level经济学:Labour market真题解析
    【无标题】
    go Session的实现(一)
    谁能真正替代你?AI辅助编码工具深度对比(chatGPT/Copilot/Cursor/New Bing)
  • 原文地址:https://blog.csdn.net/LEE180501/article/details/126235877