关于操作符的笔记和理解

博客介绍了C语言中的移位操作符、位操作符、逗号表达式等操作符的使用,包括左移、右移、按位与、按位或等。还阐述了操作符的优先级和结合性,以及表达式求值中的整型提升和算术转换。最后通过数值交换、求二进制中1的个数等习题加深对操作符的理解。

文章目录

  • 移位操作符(只针对整数)
      • <<左移操作符
      • >>右移操作符
  • 位操作符
      • &:按位与
      • |:按位或
      • ^:按位异或
      • ~:按位取反
        • 一些结论
  • 逗号表达式
  • 操作符的属性
      • 优先级
      • 结合性
  • 表达式求值
      • 整型提升
      • 算术转换
  • 一些习题
      • 数值交换
      • 求一个数二进制表达式中1的个数
      • 求单身狗
      • 1变0,0变1

由于数据在电脑中都是以补码的形式存储的,所以以下操作符都是对补码进行操作

移位操作符(只针对整数)

<<左移操作符

左移操作符可以让整个数往左移若干位
如5<<2,得到的结果是20
在这里插入图片描述
前面被移出的位丢弃,后面往后加0

>>右移操作符

右移操作符分为两类,逻辑右移和算数右移,不同的编译器实现的功能不同

  • 逻辑右移:和左移类似,直接往右移动若干位,右边多出来的空位补0(这样也就容易出现负数右移之后变成正数的情况)
  • 算术右移:往右移动若干位,左边空出来的位置补符号数
    如图,5>>2,结果是1
    在这里插入图片描述
    -1>>2,前面两个补1,结果还是-1
    在这里插入图片描述
    注意:不要移动负数位,没有这种规则

位操作符

&:按位与

逐步比较两个数的每一位,如果有0则结果为0,如果两个都是1则结果为1
在这里插入图片描述

|:按位或

逐步比较两个数的每一位,如果有1则结果为1,如果两个都是0则结果为0
在这里插入图片描述

^:按位异或

逐步比较两个数的每一位,如果相同则结果为0,如果不同则结果为1
在这里插入图片描述

~:按位取反

把传入数的每一位都颠倒过来
在这里插入图片描述

一些结论
  • a^0=a
  • a^a=0
  • a&1=末位数字
  • a&(a-1)=去掉最后面的1得到的数

逗号表达式

在这里插入图片描述
此时c被赋上了b的值,同时前面的a++也执行过了
说明逗号表达式中是以最后一个式子为准,但是前面的表达式依然会执行

逗号表达式可以让循环变得更加简洁

#include<stdio.h>

int main()
{
   int a = 0;
   scanf("%d", &a);
   if(test(a), a++, a > 0)
   {
      printf("hello");
   }
   return 0;
}

此时在if语句中,先调用了test函数和a++,最后的语句才是真正的判断条件
不然这样写显得冗余

#include<stdio.h>

int main()
{
   int a = 0;
   scanf("%d", &a);
   test(a);
   a++;
   if(a > 0)
   {
      printf("hello");
      test(a);
      a++;
   }
   return 0;
}

[]:下标访问操作符
操作数是数组名和索引值

():函数调用操作符
操作数是函数名和传入的参数

操作符的属性

优先级

和数学里乘除比加减更加优先一样,C语言中各种操作符的优先级也不一样
常用操作符优先级如下:

  1. 圆括号()
  2. 自增运算符 ++,自减运算符 –
  3. 单目运算符 + -(表示正负)
  4. 乘法 * ,除法 /
  5. 加法 +,减法 -
  6. 关系运算符 < >
  7. 赋值运算符 =

其中圆括号优先级较高,可以改变其他操作符的优先级

  • 优先级只是相邻操作符的比较,如果不相邻,就有可能产生歧义
    a*b + c*d + e*f,第一个加号和最后一个乘号的优先级不好比较,不同的编译器可能产生不一样的运算路径

结合性

a + b + c,编译器是从左往右算的,因为加法的结合性是从左往右
在这里插入图片描述
参考网站:
https://zh.cppreference.com/w/c/language/operator_precedence

表达式求值

整型提升

  • C语言中的运算通常至少以缺省整形类型(int类型)的精度来进行
  • 在CPU中,加法等运算的操作数长度一般是四个字节(int类型的长度)
  • 而如果要将两个char类型的数进行运算,就需要进行整型提升,也就是char或者short类型的操作数在使用前被调整成int类型

两个char类型的数先是将长度变为四个字节(其中有符号数补符号数,无符号数补0)变成普通整形
运算后结果被截断留后面八位(一个字节)存储下来

算术转换

如果操作数之间各自属于不同的类型,就要转化成同一种类型再运算
如整数/浮点数,整型变成了浮点型方能计算出结果
以下层次体系为寻常算术转换,下面的类型会被转化成上面的

  1. long double
  2. double
  3. float
  4. unsigned long int
  5. long int
  6. unsigned int
  7. int

一些习题

数值交换

在不创建新的变量的情况下,将两个数的值交换

#include<stdio.h>

int main()
{
   int a = 3;
   int b = 5;
   a = a ^ b;
   b = a ^ b;
   a = a ^ b;
   printf("%d\n", a);
   printf("%d\n", b);
   return 0;
}
  • 首先要知道按位异或是遵循交换律的,也就是a ^ b ^ c = c ^ a ^ b
  • 对于第二个式子来说,就相当于是把a ^ b ^ b的值赋给b,达到了把a赋给b的效果,此时b的值为a
  • 对于第三个式子来说,就相当于是把a ^ b ^ a的值赋给a,完成交换

求一个数二进制表达式中1的个数

#include<stdio.h>

int main()
{
   int a = 0;
   int count = 0;
   scanf("%d", &a);
   for(a)
   {
      a = a & (a - 1);
      count++;
   }
   printf("%d\n", count);
   return 0;
}

这里运用的是a & (a - 1)可以使a去掉最后一个1的性质,不断地去掉一个1
相比不断模2余1再除以2更加方便

求单身狗

int singleNumber(int nums[], int numsSize),找出那个只出现一次的元素。
比如:nums = [4,1,2,1,2]
返回:4

#include<stdio.h>

int singleNumber(int nums[], int sz)
{
   int i = 0;
   int m = 0;
   for(i = 0; i <= sz; i++)
   {
      m ^= nums[i];
   }
   return m;
}
int main()
{
   int nums = [4, 1, 2, 1, 2];
   int sz = sizeof(nums) / sizeof(nums[0]);
   printf("%d\n", singleNumber(nums, sz));
   return 0;
}

在这里运用了a ^ a = 0,a ^ 0 = 0,以及交换律的性质,把那些出现过两遍的数全部干掉,剩下的那个就是只出现过一次的

1变0,0变1

给定一个数,如13,把它第五位改成1,再改成0

#include<stdio.h>

int main()
{
   int a = 13;
   a = a | (a << 4);
   printf("%d\n", a);
   a = a & ~(a << 4);
   printf("%d\n", a);
   return 0;
}
  • 第一部分的原理是0000 1101按位或0001 0000,而0001 0000是1左移四位得到的
  • 第二部分的原理是0001 1101按位与1110 1111,而1110 1111又是0001 0000按位取反得到的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值