C语言操作符详解(13)

news2024/9/20 1:32:03

文章目录

  • 前言
  • 一、二进制和进制转换
    • 2进制转10进制
    • 10进制转2进制
    • 2进制转8进制
    • 2进制转16进制
  • 二、原码、反码、补码
  • 三、移位操作符
    • 左移操作符
    • 右移操作符
  • 四、位操作符
    • &
    • ^
    • ~
    • 一道奇葩的面试题
    • 一道练习题
    • 再来一个练习题
  • 五、逗号表达式
  • 六、结构成员访问操作符
    • 结构体
      • 结构的声明
      • 结构体变量的初始化
    • 结构体成员的访问
    • 直接访问
    • 间接访问
  • 七、优先级和结合性
    • 优先级
    • 结合性
  • 八、表达式求值
    • 整型提升
    • 算术转换
  • 九、几个问题表达式解析
    • 表达式1
    • 表达式2
    • 表达式3
    • 表达式4
  • 总结


前言

  其实之前我们也有过操作符的讲解,今天我们再来深入地了解一下它


一、二进制和进制转换

  生活中经常能看到2进制、8进制、10进制和16进制,说到底不过是数值的不同表示形式而已

举例来说,数值15的各种进制的表示形式:
在这里插入图片描述
但在这里我还是想重点介绍一下二进制,10进制中数字满10进1,10进制的数字每一位都是由0~9的数字组成
二进制也是如此,2进制满2进1,每一位数字也都是由0~1来组成

2进制转10进制

10进制的123表示的值是一百二十三,为什么是这个值呢?其实10进制的每一位是权重的,10进制的数字从右向左依次是个位、十位、百位,每位的权重是100,101,102
在这里插入图片描述
同样的,对于2进制的1101,我们该怎么理解呢?
在这里插入图片描述
这就是2进制转10进制的方法

10进制转2进制

一个数,比如说123,除以2余1,因为二进制上每一位的权重都是偶数,所以说必然最右位为1,这时候,我们把这个数的二进制位往右移动一位(其实就是/2),就又可以判断新的数的最后一位(即原来数的倒数第二位)是1还是0,依次类推
如图所示
在这里插入图片描述

比如说来个数字17,二进制位表示是10001,我们把它%2,为1,因此最右边一位为1,这时候把17给/2,得到8,再%2,得到0…如此,我们按顺序得到了10001,我们得到的其实是从右往左的,所以我们倒着来看,结果是10001,验证一下,1 * 16 + 1 * 1 = 17,没错

2进制转8进制

8是2的三次方,而8进制的数字每一位是0~7的,如果把这八个数字写成二进制数,有三位就足够了,比如7的二进制是111,所以在2进制转8进制的时候,从2进制序列中右边低位开始向左每3个二进制位会换算成一个8进制位的,剩余不够3个2进制位的直接换算
来个具体例子:

在这里插入图片描述

0153,0开头的数字会被认为是8进制

2进制转16进制

16是2的四次方,16进制的数字每一位是0~9,a-f的,把它们各自写成2进制,最多有4个2进制位就足够了,比如f的二进制是个1111,所以在2进制转16进制的时候,从2进制序列中右边低位开始向左每4个2进制位会换算成一个2进制位,剩余不够4个二进制位的直接换算
来个具体例子:
在这里插入图片描述

0x6b,0x开头的数字会被认为是16进制

二、原码、反码、补码

整数的2进制表示方法有三种,即原码、补码、反码
有符号整数的表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当作符号位,剩余的都是数值位

其中,符号位都是用0表示“正”,用1表示“负”

正整数的原、反、补码都相同
负整数的三种表示方法各不相同
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就得到补码

其实,补码得到原码也可以取反 ,+1

对于整型来说,数据存放内存中其实存放的是补码

我们要思考,这是为什么呢?
其实,在计算机系统中,数值一律用补码来表示和存储,原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
比如说1 + (-1)用原码的话,得到-2,这显然错误
在这里插入图片描述
反之,如果选择了补码(最高位1进制进出去了,哈哈):在这里插入图片描述
其实可以想到,早期计算机科学家想到补码得花多大精力,敬佩!

三、移位操作符

左移操作符

规则:左边抛弃、右边补0
在这里插入图片描述
结果就像这样:
在这里插入图片描述
其实二进制往左移动一位,是不是相当于每一位上的权重加了一个量级,比如说原先最左边的1,表示1个8,往左移动,就变成了1个16,其他位也如是,所以整体来看,往右移动就相当于是* 2,请记下这个结论!

右移操作符

规则:
逻辑右移:左边用0填充,右边抛弃
算术右移:左边用该值的符号位填充,右边丢弃

到底是哪一种移法,取决于编译器的实现,大部分是算术右移,VS也是
也好理解,逻辑右移有点太粗暴了,可能一开始是负数,移动一下,变成了一个超大正数

另外,对于移位运算符,不要移动负数位,这个是标准未定义的
在这里插入图片描述

四、位操作符

位操作符的操作数必须是整数,且操作的都是二进制位
在这里插入图片描述

&

规则是按照二进制位与

只要有0就是0,两个同时为1才是1

我们来求 3 & (-5)
在这里插入图片描述
显然最后得出的是3,二进制位如下,经过程度打印即可验证
在这里插入图片描述

^

规则是按照二进制位异或
相同为0,相异为1
显然,最后得出的结果:
在这里插入图片描述
我们把它取反再+1变成原码,得到以下结果-8:
在这里插入图片描述

~

首先,我们要再次强化一个认识,就是数据在内存中是以补码的形式进行存储的!
我们现在来两个变量a,b,并且假设a为1,b = ~a
那么a,b在内存中的存储是:
在这里插入图片描述
那么b实际上是多少?我们来取反+1求原码
在这里插入图片描述
答案是-2

再来个例子,~0打印出来是多少?
哈哈,答案是-1,请自行验证吧!

一道奇葩的面试题

不能创建临时变量(第三个变量),实现两个整数的交换
讲解之前,请先了解异或的两个性质,它们很好证明,你第一次见的话,可以把它记下来

a ^ 0 = a
a ^ a = 0

于是,我们可以利用整体代换思想,写出以下语句:

a = a ^ b;
b = a ^ b;
a = a ^ b;

我们把第一句的a替换到第二局,那么a ^ b ^ b(都是最开始的值)的值就赋给b,显然,b就成了原来a,a最后也变成了原来b

其实还有一种方式如下
在这里插入图片描述
但是这种写法的缺陷是,a和b如果非常大,求和后的结果超过了整型的最大值

一道练习题

编写代码实现:求一个整数存储在内存中的二进制中1的个数
我们发现,a & 1 == 1 ; a & 1 == 0,分别说明a的二进制中最低位是1,最高位是0
因此,我们确保循环条件num > 0,每次都取最后一个数来判断,并右移一位
代码如下:
在这里插入图片描述
但是我们也需要考虑正负数的区别,假如我们把把求二进制位1的个数封装成一个函数,输入-1,结果求出来是0(-1 & 2永远不会等于1),这时候,我们怎么办呢?
答案是将这个负数看成是一个无符号整数,传递过去的数字视为unsigned int,即:
在这里插入图片描述

另外,请注意n = n & (n - 1)这个操作,它的意义是去掉n最右边的一个1
因此,这种也是一个好思路,且不用再传递无符号整数了
在这里插入图片描述

再来一个练习题

结合前文的n = n & (n - 1)这个结论,请你判断一个数是否是2的次方数
其实,也就是看一个数的二进制位是否只有一个1,那么我们来一次去掉1操作,判断是否为0不就可以了?
确实是这样:
在这里插入图片描述?

我们不禁也思考,二进制位置0或者置1怎么办?
如下,原理大家自行分析
请注意,二进制的相关操作在单片机和嵌入式里面相当常见
在这里插入图片描述

五、逗号表达式

exp1, exp2, exp3,…expN

逗号表达式,就是用逗号隔开的多个表达式,逗号表达式就是从左向右执行,整个表达式的结果是最后一个表达式的结果
可用来减少冗余代码,下面是具体例子,解决了必须先操作一次的问题
在这里插入图片描述在这里插入图片描述

六、结构成员访问操作符

结构体

C语言已经提供了内置类型,如:char、short、int、long等,但是只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的,C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型

结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量,如:标量、数组、指针,甚至是其他结构体

结构的声明

在这里插入图片描述
假设要描述一个学生:
在这里插入图片描述

注意,这时候类型是struct Stu而不是Stu
申请一个变量的时候,要struct Stu s1;

结构体变量的初始化

我们可以用大括号来初始化结构体变量,如下:
在这里插入图片描述
以下可以让你明白结构体的一些知识:
在这里插入图片描述

结构体成员的访问

直接访问

使用结构成员访问操作符.

格式为结构体变量.成员名

在这里插入图片描述

间接访问

有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针
按道理来说,我们这时候需要(*结构体指针).成员名,可是,我们有一个符号->,可以直接访问
使用方式为结构体指针->成员名

七、优先级和结合性

这两个属性决定了表达式求值的计算顺序

优先级

指的是一个表达式包含多个运算符,哪个运算符应该优先执行,各种运算符的优先级是不一样的

结合性

如果两个运算符优先级相同,优先级没办法确定先执行哪一个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执行顺序,大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),比如说赋值运算符=
下面有个表达,大家有需要自行查询即可
在这里插入图片描述
具体可参考链接:
C语言运算符优先级

其实,我的原则一直都是肝踏马的优先级,直接加括号就完事儿了,乐~

八、表达式求值

整型提升

C语言中整型算术运算总是至少以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符合短整型操作数在使用之前被转换成普通整型,这被称为整型提升

即char 和 short也要先转换为整型操作数的标准长度

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

软硬件相互成就,也相互限制

我们不妨来个具体例子
char a,b,c;

a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算

那么如何进行整数提升?

1.有符号整数提升是按照变量的数据类型的符号位提升的
2.无符号整数提升,高位补0

对于负数:
在这里插入图片描述
对于正数:
在这里插入图片描述
char能存多少就存多少,其他发生截断,提升的时候就看最高位,把它当作符号位(signed char),至于无符号,直接全部补0

我们假设char c1 = 125, c2 = 10, c3 = c1 + c2;
那么我们现在打印有符号整数c3,结果是多少
分析如下,答案是-121
在这里插入图片描述

另外,如果直接直接打印c1 + c2,而不是赋给c3呢?
答案是135,至于理由自行分析!
注意此时不发生截断,只有整型提升

算术转换

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

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

九、几个问题表达式解析

表达式1

在这里插入图片描述
到底哪个运算顺序对?可是运算优先级判断的是相邻操作符的优先级,可本题确不属于该情况
因此优先级无法保证第三个*比第一个+早执行
所以计算机计算该表达式的顺序就可能为以下两种情况:
在这里插入图片描述

表达式2

在这里插入图片描述
同上,操作符的优先级只能保证前置–的运算在+前面,但是我们没办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,这是未定义的,也就是说,4 + 4还是5 + 4这是不确定的

表达式3

在这里插入图片描述
谭浩强高徒,同学同事老师谁这么写,墙壁!
这是未定义行为,具体取决于编译器,如下:
在这里插入图片描述

表达式4

在这里插入图片描述
的确,根据优先级,我们会先算后面两个fun(),可问题是,函数的调用先后顺序无法由操作符的优先级确定
所以说, 4 - 2 * 3还是 3 - 2 * 4这是不确定的

总而言之,不要写出特别复杂的表达式,尽量拆成多个语句,因为我们虽然有优先级和结合性,依然不能通过操作符的属性来确定唯一的计算路径,有潜在风险


总结

  操作符的学习,说实话是有些枯燥的,感觉动脑的还是比较少,但是别急
  我们接下来就要迎来指针的学习了,这很重要,你必须深刻掌握它,于是我先预告一下

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汇编调用C库函数—printf、scanf和Win32API

RadASM: ;右键 -> 生成依赖项 -> 生成自定义 -> masm .586 .model flat,stdcall option casemap:noneinclude msvcrt.inc include Kernel32.inc include User32.incincludelib msvcrt.lib includelib Kernel32.lib includelib User32.libscanf proto c :ptr,:vararg …

Java 的Swing 之JFrame快速入门

3、讲原件添加到当前窗口当中 //讲原件添加到当前窗口当中 this.add(btnlong); this.add(btnreset); this.add(usertext); this.add(passtext); this.add(username); this.add(password); this.setVisible(true); 4、对对应按钮设置动作监听 btnlong.addActionListene…

足球大小球及亚盘数据分析与机器学习实战详解:从数据清洗到模型优化

本文将深入探讨Java在数据分析和机器学习中的实际应用,涵盖数据预处理、模型训练和优化等方面的内容。通过详尽的代码示例,帮助读者掌握相关技术并应用于实际项目中。 数据分析、初盘数据、走地数据、分析管理系统、AI大模型预测系统、全自动化下单系统…

直播相关02-录制麦克风声音,QT 信号与槽,自定义信号和槽

一 信号与槽函数 #include "mainwindow.h" #include <QPushButton> #include <iostream> using namespace std;//我们的目的是在 window中加入一个button&#xff0c;当点击这个button后&#xff0c;关闭 MainWindow 。 MainWindow::MainWindow(QWidget …

【实施文档】软件项目实施方案(Doc原件2024实际项目)

软件实施方案 二、 项目介绍 三、 项目实施 四、 项目实施计划 五、 人员培训 六、 项目验收 七、 售后服务 八、 项目保障措施软件开发管理全套资料包清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&am…

【文献阅读】Unsupervised Machine Learning for Bot Detection on Twitter

Abstract 引入新特征&#xff0c;并降低所提模型的复杂性&#xff0c;从而提高基于聚类算法的机器人识别准确性。 最小化数据集维度和选择重要特征来实现的。 实验证明该方法的特征可以与四种不同的聚类技术&#xff08;agglomerating、k-medoids、DBSCAN 和 K-means&#x…

Android Graphics 显示系统 - VirtualDisplay mirrorDisplay 简单示例

“ Life is like a box of chocolates, you never konw what youre going to get。最近我也得到了一块巧克力&#xff0c;迫不及待地想尝一下甜的惊喜 。” 前言 上一篇文章&#xff0c;我们分享了一个VirtualDisplay的简单实例&#xff0c;主要是为了引入创建虚拟屏幕都使用了…

C# 如何检查两个给定的线段是否相交(How to check if two given line segments intersect)

给定两条线段(p1, q1)和(p2, q2)&#xff0c;判断给定的线段是否相交。 在讨论解决方案之前&#xff0c;让我们先定义方向的概念。平面中有序点三元组的方向可以是 –逆时针 –顺时针 –共线 下图显示了&#xff08;a&#xff0c;b&#xff0c;c&#xff09; 的不同可能方…

多进程批量下载era5再分析数据

1.配置key https://cds.climate.copernicus.eu/api-how-to 获取key 修改配置文件&#xff0c;把url和key复制进行 vim $HOME/.cdsapirc2.下载 根据要求修改年和月份等变量 import cdsapi import calendar import concurrent.futures import osdef download_month_data(year,…

KEIL编译生成.bin文件的简单方法

fromelf --bin -o "$LL.bin" "#L" 如图 如果不行请尝试其他方法

大模型算法入行转行?我建议你这样做!

最近私信问我关于入行、转行方面的问题比较多&#xff0c;就专门写一篇讲讲我的理解。 首先说明一下个人的背景和现状&#xff0c;我本人是本科学历&#xff0c;有互联网大厂搜推方向经验&#xff0c;后来跳到中厂继续做推荐&#xff0c;去年开始做大模型。现在是个小组长&…

c中 int 和 unsigned int

c语言中&#xff0c;char、short、int、int64以及unsigned char、unsigned short、unsigned int、unsigned int64等等类型都可以表示整数。但是他们表示整数的位数不同&#xff0c;比如&#xff1a;char/unisigned char表示8位整数&#xff1b; short/unsigned short表示16位整…

桂林自闭症寄宿学校:用关爱点亮未来

在桂林这座风景如画的城市中&#xff0c;隐藏着一所特别的学校&#xff0c;它以无尽的关爱与专业&#xff0c;为自闭症儿童撑起了一片希望的天空&#xff0c;这就是星贝育园自闭症儿童寄宿制学校。在这里&#xff0c;每一个孩子都是独一无二的&#xff0c;他们被温柔以待&#…

仪器计量校准的设备保养方法有哪些?

仪器校准、检定&#xff0c;是对设备和仪器进行校正和校验。与规范所再现的相应值相关联的一组检测&#xff0c;以规定精确测量仪器或检测系统所指示的值&#xff0c;及产品测量仪器和对照化学物质所隐含的值&#xff0c;是否符合所要求的标准。 仪器校准可能包括以下过程&…

postgresql|数据库|pg_repack和idle_in_transaction_session_timeout参数的关系

一、问题描述 在使用pg_repack这个工具做数据库的表膨胀清理过程中&#xff0c;经常会遇到类似这样的警告&#xff1a; 这里的警告表明在膨胀治理的时候&#xff0c;此表遇到了事务阻塞&#xff0c;而此时我们有三种选择&#xff0c;第一个选择是等待该事务结束&#xff0c;第…

在Excel里制作简单游戏界面

生成随机激活码 找工具箱 插入按钮 建宏 方法一&#xff1a;新建按钮的时候创建宏 方法二&#xff1a;右键->指定宏 VBA VBA代码界面 调整字体 VBA代码 Public str As String 存储激活码显示的字符 Public st As String 中间变量&#xff0c;用来替代随机数 Public ot…

[实践应用] 深度学习之激活函数

文章总览&#xff1a;YuanDaiMa2048博客文章总览 深度学习之激活函数 激活函数基本概念分类常见的激活函数2. Tanh/双曲正切激活函数3. ReLU激活函数4. Softmax激活函数 PyTorch中如何使用1. 线性激活函数2. 非线性激活函数SigmoidTanhReLULeaky ReLUParametric ReLU (PReLU) 使…

ThinkPHP Email功能如何配置才能发送邮件?

ThinkPHP Email发送流程&#xff1f;使用ThinkPHP发Email方法&#xff1f; ThinkPHP作为一款流行的PHP框架&#xff0c;提供了强大的Email功能&#xff0c;使得开发者能够轻松实现邮件发送。AokSend将详细介绍如何配置ThinkPHP Email功能&#xff0c;以确保邮件能够顺利发送。…

基于多种智能优化算法优化BP神经网络的数据时序预测

基于多种智能优化算法优化BP神经网络进行数据时序预测的研究&#xff0c;旨在通过引入多种优化算法来提高传统BP神经网络&#xff08;Backpropagation Neural Network&#xff09;的预测精度与泛化能力。 代码原理及流程 1. BP神经网络简介 BP神经网络是一种常见的前馈神经网…

别找了!包含gpt在内的国内可以使用的Ai网站都在这了【最新可用】

在当今人工智能迅速发展的时代&#xff0c;智能创作与对话平台为用户提供了多样化的功能支持。以下是一些国内代表性的GPT平台&#xff0c;涵盖了从个人到企业的广泛需求&#xff0c;您可以根据自己的需求灵活选择。我们还为您整理了这些平台的链接&#xff0c;方便直接体验。&…