C语言第十五弹---操作符(上)

✨个人主页:熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

操作符

1、操作符的分类

2、二进制和进制转换

2.1、2进制转10进制

2.1.1、10进制转2进制数字

2.2、2进制转8进制和16进制

2.2.2、2进制转16进制

[3. 原码、反码、补码](#3. 原码、反码、补码)

[4. 移位操作符](#4. 移位操作符)

[4.1 左移操作符](#4.1 左移操作符)

[4.2 右移操作符](#4.2 右移操作符)

[5. 位操作符:&、|、^、~](#5. 位操作符:&、|、^、~)

[6. 单目操作符](#6. 单目操作符)

[7. 逗号表达式](#7. 逗号表达式)

总结


1、操作符的分类

• 算术操作符: + 、 - 、 * 、 / 、 %
• 移位操作符: << >>
• 位操作符: & | ^ ~
• 赋值操作符: = 、 += 、 -= 、 *= 、 /= 、 %= 、 <<= 、 >>= 、 &= 、 |= 、 ^=
• 单目操作符: !、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、 ( 类型 )
• 关系操作符: > 、 >= 、 < 、 <= 、 == 、 !=
• 逻辑操作符: && 、 ||
• 条件操作符: ? :
• 逗号表达式: ,
• 下标引用: \[\]
• 函数调用: ()
• 结构成员访问: . 、 ->
上述的操作符,我们已经讲过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符,今天继续介绍⼀部分,操作符中有⼀些操作符和⼆进制有关系,我们先铺垫⼀下⼆进制的和进制转换的知识。

2、二进制和进制转换

其实我们经常能听到2进制、8进制、10进制、16进制这样的讲法,那是什么意思呢? 其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。
比如:数值15的各种进制的表示形式:

复制代码
15的2进制:1111
15的8进制:17
15的10进制:15
15的16进制:F

我们重点介绍⼀下⼆进制:
首先我们还是得从10进制讲起,其实10进制是我们生活中经常使用的,我们已经形成了很多尝试:
• 10进制中满10进1
• 10进制的数字每一位都是0~9的数字组成
其实二进制也是⼀样的
• 2进制中满2进1
• 2进制的数字每⼀位都是0~1的数字组成
那么 1101 就是⼆进制的数字了。

2.1、2进制转10进制

其实10进制的123表示的值是⼀百⼆⼗三,为什么是这个值呢?其实 10进制的每⼀位是权重的,10进制的数字从右向左是个位、⼗位、百位....,分别每⼀位的权重是 10^0, 10^1, 10^2...
如下图:

2进制和10进制是类似的,只不过2进制的每⼀位的权重, 从右向左是: 2^0, 2^1, 2^2...
如果是2进制的1101,该怎么理解呢?

2.1.1、10进制转2进制数字

2.2、2进制转8进制和16进制

8进制的数字每⼀位是0~7的,0~7的数字,各自写成2进制,最多有3个2进制位就足够了 ,比如7的⼆进制是111,所以在2进制转8进制数的时候, 从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算。
如:2进制的01101011,换成8进制:0153, 0开头的数字,会被当做8进制。

2.2.2、2进制转16进制

16进制的数字每⼀位是0~9,a ~f 的,0~9,a ~f的数字,各自写成2进制,最多有4个2进制位就足够了, 比如 f 的⼆进制是1111,所以在2进制转16进制数的时候, 从2进制序列中右边低位开始向左每4个2进制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
如:2进制的01101011,换成16进制:0x6b, 16进制表示的时候前面加0x

3. 原码、反码、补码

整数的2进制表示方法有三种,即原码、反码和补码。
有符号整数 的三种表示方法均有 符号位和数值位两部分 ,2进制序列中,最高位的1位是被当做符号
位,剩余的都是数值位。
符号位都是用0表示"正",用1表示"负"。
正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
反码得到原码也是可以使用:取反,+1的操作。
对于整形来说:数据存放内存中其实存放的是补码。
为什么呢?
在计算机系统中,数值⼀律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统⼀处理;同时,加法和减法也可以统⼀处理(CPU只有加法器),此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
此处只讲解原反补码的基本概念,后面章节会详细讲解如何进行计算。

4. 移位操作符

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

4.1 左移操作符

移位规则:左边抛弃、右边补0

复制代码
#include <stdio.h>
int main()
{
 int num = 10;
 int n = num<<1;
 printf("n= %d\n", n);
 printf("num= %d\n", num);
 return 0;
}

4.2 右移操作符

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

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

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

    #include <stdio.h>
    int main()
    {
    int num = -10;
    int n = num>>1;
    printf("n= %d\n", n);
    printf("num= %d\n", num);
    return 0;
    }

此处暂时记住结论,VS右移时遵守算术右移。后序会详细讲解。


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

复制代码
int num = 10;
num>>-1;//error

5. 位操作符:&、|、^、~

位操作符有 (都是使用二进制计算) :

复制代码
& //按位与    1&1=1 1&0=0 0&0=0 同时为真才为真
| //按位或    1|0=1 1|1=1 0|0=0 一个为真则为真
^ //按位异或  相同为0 相异为1
~ //按位取反  ~0为1 ~1为0

注:他们的操作数必须是整数。
直接上代码:

复制代码
#include <stdio.h>
int main()
{
 int num1 = 3;
 int num2 = 5;
 printf("%d\n", num1 & num2);
 printf("%d\n", num1 | num2);
 printf("%d\n", num1 ^ num2);
 printf("%d\n", ~0);
 return 0;
}


初次学习位运算建议用正数举例,因为原反补码相同,更容易理解。
注:屏幕上打印的是原码,计算的是补码,在内存中存储的是补码。
⼀道变态的面试题:
不能创建临时变量(第三个变量),实现两个数的交换。

复制代码
#include <stdio.h>
int main()
{
 int a = 10;
 int b = 20;
 a = a^b;
 b = a^b;
 a = a^b;
 printf("a = %d b = %d\n", a, b);
 return 0;
}


练习1:编写代码实现:求⼀个整数存储在 内存中(补码) 的⼆进制中1的个数。

复制代码
参考代码:
//方法1
#include <stdio.h>
int main()
{
 int num = 10;
 int count= 0;//计数
 while(num)
 {
 if(num%2 == 1)
 count++;
 num = num/2;
 }
 printf("二进制中1的个数 = %d\n", count);
 return 0;
}
//思考这样的实现⽅式有没有问题?

如上图所示,如果为-10,次代码就有问题,因为负数原反补码不相同。

复制代码
//方法2:
#include <stdio.h>
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 for(i=0; i<32; i++)
 {
 if( num & (1 << i) )
 count++; 
 }
 printf("⼆进制中1的个数 = %d\n",count);
 return 0;
}
//思考还能不能更加优化,这⾥必须循环32次的。



//⽅法3:
#include <stdio.h>
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 while(num)
 {
 count++;
 num = num&(num-1);
 }
 printf("⼆进制中1的个数 = %d\n",count);
 return 0;
}
//这种⽅式是不是很好?达到了优化的效果,但是难以想到。

练习2:⼆进制位置0或者置1
编写代码将13⼆进制序列的第5位修改为1,然后再改回0

复制代码
13的2进制序列: 00000000000000000000000000001101
将第5位置为1后:00000000000000000000000000011101  
将第5位再置为0:00000000000000000000000000001101


参考代码:

复制代码
#include <stdio.h>
int main()
{
 int a = 13;
 a = a | (1<<4);
 printf("a = %d\n", a);
 a = a & ~(1<<4);
 printf("a = %d\n", a);
 return 0;
}

6. 单目操作符

单目操作符有这些:
!、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、 ( 类型 )
单目操作符的特点是只有⼀个操作数,在单目操作符中 只有&和*没有介绍,这2个操作符,我们放在学习指针的时候学习。

7. 逗号表达式

复制代码
exp1, exp2, exp3, ...expN

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

复制代码
//代码1
#include<stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	//c是多少?
	printf("c = %d\n", c);
	return 0;
}


注:逗号表达式需要小括号包括喔,不然就不是逗号表达式啦!!!

复制代码
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);//此处为逗号表达式
int c = a > b, a = b + 10, a, b = a + 1;//此处为有问题表达式,a和b都重定义了
//此条代码的实质为下:
int c=a >b;
int a=b + 10;//前面已经定义a了,还定义一次所以导致重定义行为
int a;
int b=a + 1;//前面已经定义b了,海淀一一次所以导致重定义行为


//代码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)
{
 //业务处理
}

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜12 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa12 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio