[C]基础8.详解操作符

  • 博客主页:算法歌者
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。

文章目录

  • 0、总结
  • 1、操作符的分类
  • 2、二进制和进制转换
    • 2.1、2进制转10进制
    • 2.2、10进制转2进制
    • 2.3、2进制转8进制和16进制
  • 3、原码、反码、补码
  • 4、移位操作符
    • 4.1 左移操作符
    • 4.2 右移操作符
    • 4.3 警告:不要移动负数位
  • 5、位操作符
    • 5.1 例1:观察位操作符
    • 5.2 例2:不能创建临时变量(第三个变量),实现两个数的交换。
    • 5.3 例3:求一个整数存储在内存中的二进制中1的个数。
    • 5.4 例4:如何判断一个数是否是2的次方数?
    • 5.5 例5:二进制位置0或者置1
  • 6、逗号表达式
  • 7、下标访问[]、函数调用()
  • 8、结构成员访问操作符
  • 9、操作符的属性:优先级、结合性
  • 10、其他
    • 10.1 整型提升
    • 10.2 算术转换


0、总结

在这里插入图片描述

1、操作符的分类

  • 算术操作符:+、-、*、/、%
  • 移位操作符:<<、>>
  • 位操作符:&、|、^、~
  • 赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
  • 单目操作符:!、++、--、&(取地址操作符)、*(解引用操作符)、+(正号操作符)、-(负号操作符)、~、sizeof、(类型)
  • 关系操作符:>、>=、<、<=、==、!=
  • 逻辑操作符:&&、||
  • 条件操作符:?:
  • 逗号表达式:,
  • 下标引用:[]
  • 函数调用:()
  • 结构成员访问:.、->

操作符中一些操作符和二进制有关系,需要先简单了解二进制和进制转换的知识。

2、二进制和进制转换

进制,是数值的不同表示形式。例如,数值15的各种进制的表示形式:

152进制: 1111
158进制: 17
1510进制:15
1516进制:F

观察10进制,得出规律如下:

  • 10进制中满10进1。
  • 10进制的数字每一位都是0~9的数字组成。

那么,二进制也是一样的:

  • 2进制中满2进1。
  • 2进制的数字每一位都是0~1的数字组成。

2.1、2进制转10进制

在10进制中,每一位是权重,10进制的数字从右向左是个位、十位、百位…,分别每一位的权重是100,101,102…。

在十进制中,如图:

假设2进制的1101,如何理解呢?如图:

2.2、10进制转2进制

从图中可以知道:余数作用是求出是当前数的个位

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

原理:二进制数可以按照3个位数(或更一般地,按照任意固定数量的位数)转换到其他进制,这是基于进制转换的基本原理和方便性考虑的。

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

注意:0开头的数字,会被当做8进制。

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

注意:16进制表示的时候前面加0x。

例如:2进制的01101011,换成16进制:0x6b。

进制转换总结如下:

  • 二进制转八进制,3位二进制
  • 二进制转十进制,权和计算法
  • 二进制转十六进制,4位二进制
  • 十进制转十六进制,先转二进制之后转十六进制
#include <stdio.h>
int main()
{
	printf("%d\n", 153);    // 十进制
	printf("%d\n", 0153);   // 八进制
	printf("%d\n", 0x153);  // 十六进制
	return 0;
}
输出:
153
107
339

3、原码、反码、补码

二进制:

  • 整数2进制表示方法:原码、反码、补码。
  • 有符号整数的2进制三种表示方法均有符号位数值位两部分。
  • 2进制序列中,最高位的1位是被当做符号位,剩余都是数值位。在符号位中,0表示“正”,1表示“负”。
  • 原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
  • 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
  • 补码:反码+1就得到补码。
  • 正整数的原、反、补码都相同。
  • 负整数的三种表示方法各不相同。

值得一提是:

  • 反码得到原码也是可以使用:取反,+1的操作。

对整形来说:数据存放内存中其实存放的是补码,对于整数的计算统一都是用补码来计算。

为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器),此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

#include <stdio.h>
int main()
{
	int a = -10;
	// -10是存放在a中,a是整型变量,是4个字节,32bit位
	// 10000000 00000000 00000000 00001010 - 原码
	// 11111111 11111111 11111111 11110101 - 反码
	// 11111111 11111111 11111111 11110110 - 补码

	int b = 10;
	// 00000000 00000000 00000000 00001010 - 原码
	// 00000000 00000000 00000000 00001010 - 反码
	// 00000000 00000000 00000000 00001010 - 补码

	return 0;
}

4、移位操作符

左移操作符:<<

右移操作符:>>

注意:移位操作符的操作数只能是整数

4.1 左移操作符

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

#include <stdio.h>
int main()
{
	int num = 10;
	// 移位规则:左边抛弃、右边补0
	int n = num << 1;
	printf("n = %d\n", n);
	printf("num = %d\n", num);
	return 0;
}
输出:
n = 20
num = 10

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;
}
输出:
n = 5
num = 10

4.3 警告:不要移动负数位

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

例如:

int num = 10;
num >> -1; //error

5、位操作符

位操作符有:

  • &:按位与
  • | :按位或
  • ^:按位异或
  • ~:按位取反

注意:他们的操作数必须是整数。

5.1 例1:观察位操作符

#include <stdio.h>
int main()
{
	// 10000000 00000000 00000000 00000011 -3的原码
	// 11111111 11111111 11111111 11111100
	// 11111111 11111111 11111111 11111101 -3的补码
	int num1 = -3;
	// 00000000 00000000 00000000 00000101  5的补码
	int num2 = 5;

	// 00000000 00000000 00000000 00000101  按位与,得出5
	printf("%d\n", num1 & num2);
	// 11111111 11111111 11111111 11111101  按位或,补码
	// 10000000 00000000 00000000 00000011  计算,反码+1,得出-3
	printf("%d\n", num1 | num2);
	// 11111111 11111111 11111111 11111000  按位异或,补码
	// 10000000 00000000 00000000 00001000  计算,反码+1,得出-8
	printf("%d\n", num1 ^ num2);
	// 00000000 00000000 00000000 00000000 0的补码
	// 11111111 11111111 11111111 11111111 ~0
	// 10000000 00000000 00000000 00000001 计算,反码+1,得出-1
	printf("%d\n", ~0);
	return 0;
}
输出:
5
-3
-8
-1

5.2 例2:不能创建临时变量(第三个变量),实现两个数的交换。

#include <stdio.h>
int main()
{
	int a = 100;
	int b = 202;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a = %d b = %d\n", a, b);
	return 0;
}
输出:
a = 202 b = 100

5.3 例3:求一个整数存储在内存中的二进制中1的个数。

方法1:

// 方法1:
#include <stdio.h>
int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;
	while (num)
	{
		if (num % 2 == 1)
			count++;
		num = num / 2;
	}
	printf("%d\n", count);
	return 0;
}

在负数中,方法1失效了,比如-1在内存中1的个数有有32,而结果是0。

继续优化,写方法2:

// 方法2:
#include <stdio.h>
int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (num & (1 << i))
			count++;
	}
	printf("%d\n", count);
	return 0;
}

方法2中是必须循环32次,再继续优化,写方法3:

// 方法3:
#include <stdio.h>
int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;
	while (num)
	{
		count++;
		num = num & (num - 1); 
	}
	printf("%d\n", count);
	return 0;
}

在方法3中,每次循环,都会将 num 的最低位的1清零,并将计数器 count 加1。

5.4 例4:如何判断一个数是否是2的次方数?

if (n & (n - 1) == 0)
{
    n就是2的次方数
}

5.5 例5:二进制位置0或者置1

编写代码将13二进制序列的第5位修改为1,然后再改回0。

132进制序列:  00000000 00000000 00000000 00001101
将第5位置为1后: 00000000 00000000 00000000 00011101
将第5位再置为000000000 00000000 00000000 00001101

代码如下:

#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、逗号表达式

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

exp1, exp2, exp3, ... expN

7、下标访问[]、函数调用()

下标引用操作符:操作数为一个数组名 + 一个索引值。

int arr[10];   // 创建数组
arr[9] = 10;   // 使用下标引用操作符
[ ] 的两个操作数是arr和9

函数调用操作符:接受一个或者多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

8、结构成员访问操作符

点操作符(.):直接访问结构体成员,点操作符接受两个操作数。

使用方式:结构体变量.成员名。如下:

#include <stdio.h>
struct Point
{
	int x;
	int y;
}p = { 1,2 };

int main()
{
	printf("x:%d, y:%d\n", p.x, p.y);
	return 0;
}
运行:
x:1, y:2

箭头操作符(->):间接访问结构体成员。 有时候我们得到的不是⼀个结构体变量,而是得到了一个指向结构体的指针。

使用方式:结构体指针->成员名。如下:

#include <stdio.h>
struct Point
{
	int x;
	int y;
}p = { 1,2 };

int main()
{
	struct Point* ptr = &p;
	ptr->x = 10;
	ptr->y = 20;
	printf("x:%d, y:%d\n", ptr->x, ptr->y);
	return 0;
}
运行:
x:10, y:20

9、操作符的属性:优先级、结合性

C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。

结合性:如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),比如赋值运算符( = )。

建议大概记住这些操作符的优先级就行,其他操作符在使用的时候查看下面表格就可以了。

• 圆括号(()

• 自增运算符(++),自减运算符(--

• 单⽬运算符(+-

• 乘法(*),除法(/

• 加法(+),减法(-

• 关系运算符(<>等)

• 赋值运算符(=

官方文档:https://zh.cppreference.com/w/c/language/operator_precedence

10、其他

10.1 整型提升

C语言中整型算术运算总是至少以缺省整型类型的精度来进行的。

缺省:在计算机科学和相关领域中,它通常指的是“默认”或“预设”的意思。

整型提升:为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purposeCPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

char a, b, c;
...
a = b + c;

b和c的值被提升为普通整型,然后再执行加法运算。加法运算完成之后,结果将被截断,然后再存储于a中。

如何进行整体提升呢?

  • 有符号整数提升是按照变量的数据类型的符号位来提升的。
  • 无符号整数提升,高位补0。
// 负数的整形提升
char c1 = -1;
整型提升:
11111111 11111111 11111111 11111111
// 正数的整形提升
char c2 = 1;
整型提升:
00000000 00000000 00000000 00000001

10.2 算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外一个操作数的类型后执行运算。


完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958786.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot用axiospost请求接收字符串参数为null的解决方案

问题 ​ 今天在用前端 post 请求后端时发现&#xff0c;由于是以 Json对象的形式传输的&#xff0c;后端用两个字符串形参无法获取到对应的参数值 前端代码如下&#xff1a; axios.post(http://localhost:8083/test/postParams,{a: 1, b:2} ,{Content-Type: application/jso…

数据结构——堆(介绍,堆的基本操作、堆排序)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

【Qt】05-菜单栏

做菜单 前言一、创建文件二、菜单栏 QMenuBar2.1 示例代码2.2 运行结果 三、工具栏 QToolBar3.1 运行代码3.2 结果分析 四、状态栏 QStatusBar4.1 运行代码4.2 运行结果 五、文本编辑框 QTextEdit5.1 运行代码5.2 运行结果 六、浮动窗口 addDockWidget6.1 运行代码6.2 运行结果…

【喜讯】海云安荣获“数字安全产业贡献奖”

近日&#xff0c;国内领先的数字化领域独立第三方调研咨询机构数世咨询主办的“2025数字安全市场年度大会”在北京成功举办。在此次大会上&#xff0c;海云安的高敏捷信创白盒产品凭借其在AI大模型技术方面的卓越贡献和突出的技术创新能力&#xff0c;荣获了“数字安全产业贡献…

MySQL训练营-慢查询诊断问题

slow_query_log long_query_time slow_query_log&#xff1a;日志开关&#xff0c;是否记慢查询日志 long_query_time&#xff1a;超过多长时间判定为慢查询 查看参数设置&#xff1a; SHOW VARIABLES LIKE ‘slow_query_log’;SHOW VARIABLES LIKE ‘long_query_time’; …

2025年最新汽车零部件企业销售项目管理解决方案

在汽车零部件企业&#xff0c;销售项目管理的不规范和销售预测的不准确性常导致生产计划无法及时调整&#xff0c;因此客户关系常常中断&#xff0c;导致企业业务机会的丧失。为解决该问题&#xff0c;企业需要投入更多资源以优化销售流程与销售预测。 1、360多维立体客户视图…

K8S中ingress详解

Ingress介绍 Kubernetes 集群中&#xff0c;服务&#xff08;Service&#xff09;是一种抽象&#xff0c;它定义了一种访问 Pod 的方式&#xff0c;无论这些 Pod 如何变化&#xff0c;服务都保持不变。服务可以被映射到一个静态的 IP 地址&#xff08;ClusterIP&#xff09;、一…

大模型:LangChain技术讲解

一、什么是LangChain 1、介绍 LangChain是一个用于开发由大型语言模型提供支持的Python框架。它提供了一系列工具和组件&#xff0c;帮助我们将语言模型集成到自己的应用程序中。 有了它之后&#xff0c;我们可以更轻松地实现对话系统、文本生成、文本分类、问答系统等功能。…

【优选算法篇】2----复写零

---------------------------------------begin--------------------------------------- 这道算法题相对于移动零&#xff0c;就上了一点点强度咯&#xff0c;不过还是很容易理解的啦~ 题目解析&#xff1a; 这道题如果没理解好题目&#xff0c;是很难的&#xff0c;但理解题…

office 2019 关闭word窗口后卡死未响应

最近关闭word文件总是出现卡死未响应的状态&#xff0c;必须从任务管理器才能杀掉word 进程&#xff0c;然后重新打开word再保存&#xff0c;很是麻烦。&#xff08;#其他特征&#xff0c;在word中打字会特别变慢&#xff0c;敲击键盘半秒才出现字符。&#xff09; office官网…

acm培训 part 1(学习总结)

第一部分的重点为语法糖&#xff0c;时空复杂度&#xff0c;stl容器等等&#xff0c;下面就简单介绍一下这些部分。 1. 语法糖 1.1 定义 语法糖是由英国计算机科学家彼得约翰兰达提出的一个术语&#xff0c;指的是编程语言中添加的某种语法&#xff0c;这种语法对语言的功能…

Arduino基础入门学习——OLED显示屏+DHT11采集温湿度并显示

Arduino基础入门学习——OLED显示屏DHT11显示温湿度 一、前言二、准备工作三、程序代码四、结束语 一、前言 本篇文章主要使用OLED液晶显示屏模块和DHT11温湿度传感器&#xff0c;获取环境温湿度并显示在显示屏&#xff0c;也算是结合之前我所编写的博客给大家带来一个算是比较…

Kubernetes相关知识入门详解

一、Pod的滚动升级 1.服务升级的一般思路&#xff1a;停止与该服务相关的所有服务pod&#xff0c;重新拉去更新后的镜像并启动。这种方法存在一个比较现实的问题是逐步升级导致较长时间的服务不可用。 2.Kubernetes滚动升级的思路&#xff1a;通过滚动升级的命令创建新的rc&…

云原生时代,如何构建高效分布式监控系统

文章目录 一.监控现状二.Thanos原理分析SidecarQuerierStoreCompactor 三.Sidecar or ReceiverThanos Receiver工作原理 四.分布式运维架构 一.监控现状 Prometheus是CNCF基金会管理的一个开源监控项目&#xff0c;由于其良好的架构设计和完善的生态&#xff0c;迅速成为了监控…

Qt 5.14.2 学习记录 —— 십구 事件

文章目录 1、事件的概念2、处理事件3、鼠标事件1、鼠标单击和双击2、鼠标移动3、鼠标滚轮滚动 4、键盘事件5、定时器事件6、窗口移动和大小改变事件 1、事件的概念 用户进行操作时会产生事件&#xff0c;事件可以关联处理函数。Qt封装了操作系统的事件机制&#xff0c;然后进一…

10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析

10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析 文章目录 10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析1. 规则持久化1.1 Nacos Server 配置中心-规则持久化实例 2. 最后&#xff1a; 1. 规则持久化 规则没有持久化的问题 如果 sentinel 流控规则没有…

地学专业想提前准备春招?怎么准备自己的简历?

眼看着即将过年&#xff0c;过完年后基本上春招也要开始提上日程 之前咱们说过&#xff0c;很多同学认为自身技术过硬就会一路顺风&#xff0c;自己经验丰富、编程技术过硬&#xff0c;就不愁找不到工作&#xff0c;这固然是取得好offer的基础。 但再好的技术也不可能通过混乱…

IoTDB结合Mybatis使用示例(增删查改自定义sql等)

IoTDB时序库是当前越来越流行以及基于其优势各大厂商越来越易接受的国产开源时序数据库&#xff0c;针对IoTDB的内容不做过多介绍&#xff0c;在使用该时序库时&#xff0c;往往有一定入门门槛&#xff0c;不同于关系型数据库或文档型数据库那般方便维护和接入开发&#xff0c;…

Go语言的栈空间管理

Go 语言的栈空间管理 Go 语言的栈空间管理是其并发模型的核心之一。Go 的运行时环境&#xff08;runtime&#xff09;采用动态栈分配机制&#xff0c;能够根据 Goroutine 的需求动态扩展和收缩栈空间&#xff0c;避免了传统固定栈大小的限制。Go 的栈管理经历了从 分块式栈 到…

细说STM32F407单片机电源低功耗StandbyMode待机模式及应用示例

目录 一、待机模式基础知识 1、进入待机模式 2、待机模式的状态 3、退出待机模式 二、待机模式应用示例 1、示例功能和CubeMX项目设置 &#xff08;1&#xff09; 时钟 &#xff08;2&#xff09; DEBUG、LED1、KeyRight、USART6、CodeGenerator &#xff08;3&#x…