4

【C语言】操作符详解(一)

 1 year ago
source link: https://blog.51cto.com/u_16189938/6983530
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.原码,反码,补码

int a=1;

整形占用四个字节----32bit

00000000 00000000 00000000 00000001  (数值位)

1.1原码,反码,补码的介绍

  • 整数的2进制表示方法有三种,即原码,反码,补码
  • 三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,⽽数值位最⾼位的⼀位是被当做符号位,剩余的都是数值位。
  • 正整数的原,反,补码都相同
  • 负整数的三种表示方法各不相同
  1. 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
  2. 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
  3. 补码:反码+1就得到补码。
【C语言】操作符详解(一)_反码
  • 原码和补码的相互转换均为:先取反,后+1.

1.2补码的作用

  • 对于整形来说:数据存放内存中其实存放的是补码。

为什么?

1. 在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;

2. 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。例如1-1可以转化为1+(-1)

#include <stdio.h>

//int main()
//{
//	int a = 5;
//	//00000000000000000000000000000101 - 原码
//	//00000000000000000000000000000101 - 反码
//	//00000000000000000000000000000101 - 补码
//
//	int b = -5;
//	//10000000000000000000000000000101 - 原码
//	//11111111111111111111111111111010 - 反码
//	//1111 1111 1111 1111 1111 1111 1111 1011 - 补码
//	//f    f    f    f    f     f    f   b
//	//0x ff ff ff fb   --->内存中的形式
//	return 0;
//}

另外在计算时补码符合计数规律

int main()
//{
//	//1-1
//	//1+(-1) = 0
//	//00000000000000000000000000000001
//	//10000000000000000000000000000001   
//	//10000000000000000000000000000010--> -2		原码相加之和
//	//
//	//00000000000000000000000000000001   1的补码
//	//11111111111111111111111111111111   -1的补码
//	//00000000000000000000000000000000		加1之后符号位的1到了第33位,消掉。补码相加之和
//由此可见补码相加才是正确的答案
//	//11111111111111111111111111111111
//	//10000000000000000000000000000000
//	//10000000000000000000000000000001
//	return 0;
//}

2.位移操作符

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

2.1左移操作符

  • 移位规则:左边抛弃、右边补0
【C语言】操作符详解(一)_操作符_02

例如:

//int main()
//{
//	int a = -10;
//	int b = a << 1;
//	//10000000000000000000000000001010
//	//11111111111111111111111111110101
//	//11111111111111111111111111110110
//	//a<<1
//	//11111111111111111111111111101100
//	//10000000000000000000000000010011
//	//10000000000000000000000000010100
//	//-20
//	printf("b = %d\n", b);
//
//	return 0;
//}

2.2右移操作符

位移规则:首先右移运算分两种

1.逻辑右移:左边⽤0填充,右边丢弃

2.算术右移:左边⽤原该值的符号位填充,右边丢弃

【C语言】操作符详解(一)_C语言_03
【C语言】操作符详解(一)_补码_04

举例如下:

//int main()
//{
//	int num = -1;
//	//10000000000000000000000000000001
//	//11111111111111111111111111111110
//	//11111111111111111111111111111111
//	//
//	int n = num >> 1;
//	printf("%d\n", n);
//
//	return 0;
//}结果仍为-1

警告⚠⚠:对于移位运算符,不要移动负数位,这个是标准未定义的。

2.3逻辑右移与算术右移的区别

  • 逻辑右移就是不考虑符号位,右移一位,左边补零即可。 算术右移需要考虑符号位,右移一位,若符号位为1,就在左边补1,;否则,就补0。
  • 算术右移也可以进行有符号位的除法,右移n位就等于除2的n次方。 例如,8位二进制数11001101分别右移一位。

3.位操作符:&(与),|(或),^(异或),~(取反)

3.1&(与):按位与 -- 对应的二进制位,有0则为0,两个同时为1才是1

int main()
{
	int a=5;
  int b=-6;
  int c=a&b;
  //00000000000000000000000000000101	5的补码
  //11111111111111111111111111111010	6的补码
  //00000000000000000000000000000000	结果
  printf("%d",c);
  return 0;
}//最终结果为0

3.2|(或):按位或 -- 对应的二进制位上,有1则为1,两个同时为0才是0

int main()
{
	int a=5;
  int b=-6;
  int c=a&b;
  //00000000000000000000000000000101	5的补码
  //11111111111111111111111111111010	6的补码
  //11111111111111111111111111111111	结果
  printf("%d",c);
  return 0;
}//最终结果为-1

3.3^(异或):按位异或 -- 对应的二进制位上,相同为0(真),相异则为1(假)

int main()
{
	int a=5;
  int b=-6;
  int c=a&b;
  //00000000000000000000000000000101	5的补码
  //11111111111111111111111111111010	6的补码
  //11111111111111111111111111111111	结果
  printf("%d",c);
  return 0;
}//最终结果为-1

3.4~(取反):按位取反 -- 二进制位,0变1,1变0

int main()
{
	int n = 0;
  int a = ~n;
  //00000000000000000000000000000000
  //11111111111111111111111111111111
  //包括符号位全部取反
  printf("%d",a);
  return 0;
}因此a=-

3.5那这为操作符有什么具体作用呢?

  • 举例1(&)
//想知道数字21的二进制序列中的最低位是几
int main()
{
	int a=21;
  //a&1
  //00000000000000000000000000010101	21的补码
  //00000000000000000000000000000001
  //00000000000000000000000000000001	结果为1
  return 0;
}
//因此我们可以通过按位与的这种方式来确定具体的某一位是几
//例如如果我们想知道第5位是几时,只需要向右移动4位
//(a>>4)&1
即可
  • 举例2(^)(难点)
题:不创建临时变量(第三个变量),实现两个数的交换
int main()
{
	int a=3;
  int b=5;
  a=a+b;
  b=a-b;
  a=a-b;
  printf("%d %d",a,b);
  return 0;
}
//可是存在一个问题:范围溢出。即当a和b过大时会出现错误。
解决方法:
int main()
{
	int a=3;
  int b=5;
  a=a^b;
  b=a^b;
  a=a^b;
  printf("%d %d",a,b);
  return 0;
}//发现也能得出结果
  • 举例3(或,取反)
int main()
{
	//将a的二进制中的di5位改成1
  int a=13;
  //00000000000000000000000000001101
  //1<<4
  //00000000000000000000000000010000
  a = a | (1<<4);
  printf("a=%d",a);//a=29
  //将a的二进制中第5位改成0
  //00000000000000000000000000001101
  //11111111111111111111111111101111  ~(1<<4)
  a = a &~(1<<4);
  printf("a=%d",a);//a=13
  return 0;
}

结论:

  • 0^a=a     a^a=0      3 ^ 3 ^ 5=5,3 ^ 5 ^ 3=5.所以得出异或支持交换律
  • 这种异或操作是有局限性的:
  1. 只能作用于整数交换
  2. 代码可读性差
  3. 代码的执行效率也是低于设置3个参数的

3.6练习:编写代码实现:求一个整数储存在内存中的二进制中1的个数。

int main()
{
	int a = 15;
  //1 1 1 1
  //8 4 2 1
  //15%2 = 1
  //15/7 = 7
  //1 1 1
  //7%2 = 1
  int count=0;
  while(a)
  {
  	if(a%2 == =)
    {
    	count++;
    }
    a = a/2;
  }
  printf("count=%d",count);
  return 0;
}但是当你输入负数的时候,就会出现错误

//法二
int main()
{
	int a = 0;
	scanf("%d", &a);
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (((a >> i) & 1) == 1)
		{
			count++;
		}
    }
	printf("%d\n", count);
	return 0;
}

//法三,mage进化
int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);//每进行一次循环都会减去一个一
		count++;
	}
	printf("%d\n", count);

	return 0;
}

4.逗号表达式

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

举例:

//代码1 

int a = 1;

int b = 2;

int c = (a>b, a=b+10, a, b=a+1);//逗号表达式 
c为13

//代码2 

if (a =b + 1, c=a / 2, d > 0)

//代码3 
a = get_val();
count_val(a);

while (a > 0)
{
 //业务处理 
 a = get_val();
 count_val(a);
}

如果使⽤逗号表达式,改写:

while (a = get_val(), count_val(a), a>0)
{
 //业务处理 
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK