C语言学习分享(第七次)------操作符

news2024/11/27 4:16:44

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C语言学习分享⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习更多C语言知识
  🔝🔝


在这里插入图片描述


操作符详解

  • 1. 前言🚩
  • 2. 移位操作符🚩
    • 2.1 右移操作符🏁
  • 3. 位操作符🚩
  • 4. sizeof和数组🚩
  • 5. 隐式类型转换🚩
    • 5.1 整型提升的意义🏁
  • 6. 操作符的属性🚩
    • 6.1 问题表达式🏁
  • 7. 总结🚩


1. 前言🚩

我们已经在了解C语言的内一章节熟悉了所有的操作符了解C语言.其实操作符还有一些更细节更有用的延申内容,本章就给大家带来操作符详解!


2. 移位操作符🚩

在这里插入图片描述
我们之前介绍过,这里的移位操作符移动的是二进制位

注意:这里的移位操作符的操作数只能是正整数.

比如说:

int a=10;
int b=a>>1;
int c=a<<-1;

这种移动负数位的行为是未定义的,在不同编译器下可能出现不同的情况


2.1 右移操作符🏁

这里的左移操作符很简单,就是左边舍弃,右边补0,然而我们的右移操作符相对比较复杂,首先右移运算分为两种:

  • 逻辑移位
    左边用0填充,右边丢弃

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

我们的编译器往往采用的是算术移位


对于逻辑移位来说,比较容易理解,和左移一样直接补0即可,但是这里的算术移位还要分情况:

int main()
{
	int a = 15;
	//00000000000000000000000000001111 - 原码
	//00000000000000000000000000001111 - 反码
	//00000000000000000000000000001111 - 补码

	int b = -15;
	//10000000000000000000000000001111 - 原码
	//11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码)
	//11111111111111111111111111110001 - 补码(反码+1就是补码)
	
	//整数在内存中存储的是补码
	//计算的时候也是使用补码计算的
	return 0;
}


这里我们首先需要知道,数据在内存中存储和运算是用的补码.假设我们将a算术右移1,再将b算术右移1,我们来看看结果:

//移位移动的是补码的二进制序列
int main()
{
	int a = 15;
	int b = a >> 1;
	printf("%d\n", b);//7
	printf("%d\n", a);//15
	return 0;
}

int main()
{
	int a = -15;
	int b = a >> 1;
	printf("%d\n", b);
	printf("%d\n", a);
	return 0;
}

结论1:

我们可以打印出这里的第一个 a b和第二个 a b,.我们发现第一个打印的a还是15,这说明这个运算: a>>1对a没有影响(不像a++会让a改变),如果我们想要a的二进制位向右移动,应该写成:a=a>>1

结论2:

当我们打印第二个 a b 时,会发现a等于-8,那么这个-8是怎么来的?这里我们画图来理解:

在这里插入图片描述

我们发现最终得到的源码为-8的源码.这里-15向右移动时,左边不是补0,而是补1,因为-15的符号位是1,所以前面补1.


3. 位操作符🚩

在这里插入图片描述

这里的按位与&和按位或|大家都比较熟悉了就不多讲解.主要是这个按位异或操作^,两个数对应的二进制位相同为0,相异为1,所以这里我们可以得出一个结论:

  • 任何数与0按位异或的结果都是这个数本身
  • 任何数按位异或自己本身的结果都为0.

知道了上面的结论后,这里我给出一道非常经典的面试题:不能创建临时变量,交换a和b的值按照我们以往的方法,我们会先将a,b中一个值保存起来然后再挨个赋值,当然我们学到位操作符后有更优解不用创建临时变量:

#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;
}

这里我们;来理解一下为什么这样可以交换两个值:
在这里插入图片描述
.我们将这几个步骤一一走下来会发现:b的值变成a了,a的值也变成b了.这里就是充分运用了一个数按位异或0和按位异或本身的结论来实现功能


其实这里还有一种解法:大家自行理解:

#include <stdio.h>
int main()
{
 int a = 10;
 int b = 20;
 a=a+b;//a=30
 b=a-b;//b=30-20=10=a
 a=a-b;//a=30-10=20=b
 printf("a = %d b = %d\n", a, b);
 return 0;
}

除此之外,这里还有一道很经典的OJ题力扣136题也可以用按位异或做出来,大家可以自己去思考


4. sizeof和数组🚩

这里直接给出一段代码再来理解它们的区别:

#include <stdio.h>
void test1(int arr[])
{
 printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
 printf("%d\n", sizeof(ch));//(4)
}
int main()
{
 int arr[10] = {0};
 char ch[10] = {0};
 printf("%d\n", sizeof(arr));//(1)
 printf("%d\n", sizeof(ch));//(3)
 test1(arr);
 test2(ch);
 return 0;
}

这里的(1)(2)(3)(4)分别打印什么呢?先来看(1)(3),我们之前提过,当数组名放在sizeof内部时代表整个数组,所以(1)就代表10个整型的大小.应该为40.(3)也是同理,代表10个字符型的大小,应该是10.


而当我们的数组名作为函数参数传到函数中时,它代表的是数组首元素地址,再将这里的数组名放在sizeof中时,这下就和我们前面的结论不同了,因为这里的数组名已经是首元素地址了,相当于它就是一个地址,而在32位机器中,地址也就是指针所占大小为4个字节,所以这里的sizeof求值相当于求得是指针得大小,就和数组得类型没有关系了,不管你是整型数组还是字符型数组,只是要指针它的大小就是4个字节(32位机器),所以(2)(4)都会打印4或8(64位机器打印8)


5. 隐式类型转换🚩

C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。比如下面这段代码:

char a = 5;
char b = 127;
char c = a + b;

这里的将a和b相加时,系统会自己进行隐式类型转换(就是不会显现出来这种转换),将char类型整型提升至int类型再进行运算.这里我们将它们的二进制码写出来做分析

int main()
{
	char c1 = 5;//char类型占一个字节,八个二进制位
	//00000000000000000000000000000101
	//00000101 - c1 (截断成8个二进制位)
	char c2 = 127;
	//00000000000000000000000001111111
	//01111111 - c2 (截断成8个二进制位)
	char c3 = c1 + c2;//进行运算时要整型提升.截断后第一位为0,前面就全部补0
	//00000000000000000000000000000101(整型提升后的5)
	//00000000000000000000000001111111(整型提升后的127)
	//00000000000000000000000010000100(相加后得到的值)并且因为c也是char类型的变量,所以得到最终的值后还要发生截断
	//10000100 - c3(截断成8个二进制位)
	
	//%d - 10进制的形式打印有符号的整数
	//11111111111111111111111110000100 - 补码
	//11111111111111111111111110000011 - 反码
	//10000000000000000000000001111100 - 源码-> -124
	printf("%d\n", c3);
	return 0;


我们总结一下隐式类型转换这个过程发生的事情:

  • 第一步: a=5是整型放在char类型中,要从四个字节(32个二进制位)截断到一个字节(8个二进制位),b和a同理

  • 第二步: 当a和b相加时,a和b会发生隐式类型转换,暂时变成整型(四个字节),然后a和b作为整型相加后,把值赋值给c

  • 第三步: c也是char类型变量,所以a和b相加后的值也要从四个字节截断到一个字节.

  • 第四步: printf函数要打印C,并且是按照整型%d打印,所以这里的C还要发生整型提升成四个字节后才能被打印

  • 第五步: C在整型提升前的二进制码是:10000100,第一个二进制码是1所以整型提升时,前面的24位二进制位全部补1

当你真正理解了上面的五步,你也许就理解隐式类型转换是怎么回事了!


5.1 整型提升的意义🏁

整型提升的意义:

  • 表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
    一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

  • 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

  • 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
    中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
    换为int或unsigned int,然后才能送入CPU去执行运算。

并且整形提升是按照变量的数据类型的符号位来提升的 ! ! !

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
 return 0;
}

6. 操作符的属性🚩

复杂表达式的求值有三个影响的因素。

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级

这里我给出一个博客链接,大家可以去看看操作符的优先级和结合性:操作符的优先级和结合性

值得注意的是,这个表格不用全文背诵,当你要用到时可以随时查表,或者你自己不确定优先级时可以根据你的目的加上相应的括号


6.1 问题表达式🏁

即使我们知道了操作符的优先级和操作符的结合性,有些表达式的值也可以得到不同的答案,比如很经典的:

int c = 5;
int a = c + --c;

这段代码在不同编译器下求出的值可能是不一样的,因为操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

我们知道这里得加号是从右至左结合得,所以先算–c,但是前面的C你并不知道它是一开始就放进去的5还是C–之后才放进去的4 !


亦或者这段代码:

int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %d\n", i);
 return 0;
}

它在不同编译器下的结果:

在这里插入图片描述

总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。这种代码是非法表达式,我们在写代码的时候宁愿多写几行也不能写成这样图快 ! ! !


7. 总结🚩

操作符这一模块使用的还是很频繁的,有些看起来很麻烦的问题用操作符的手法来解决可能异常简单


💕 我的码云:gitee-杭电码农-NEO💕

🔎 下期预告: 初阶指针详解🔍

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

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

相关文章

Ajax,前后端分离开发,前端工程化,Element,Vue路由,打包部署

Ajax介绍 Axios <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

为什么我掌握了大量软测知识,却还是找不到工作?

很多朋友都在疑惑&#xff0c;为什么随着对于软件测试了解的加深&#xff0c;不断掌握更多测试知识与技巧&#xff0c;找工作貌似越来越难了&#xff1f; 不免让人联想到最近偶然间看到一句话&#xff1a;“软件测试是整个 IT 行业中最差的岗位”。 打工人的问题出在哪&#xf…

使用Jmeter应该如何进行http接口性能测试?

在进行网页或应用程序后台接口开发时&#xff0c;一般要及时测试开发的接口能否正确接收和返回数据&#xff0c;对于单次测试&#xff0c;Postman插件是个不错的Http请求模拟工具。 但是Postman只能模拟单客户端的单次请求&#xff0c;而对于模拟多用户并发等性能测试&#xff…

11.1网络编程——

多线程 一、基础知识概念相关API二、任务创建一个简单的本地客户端创建一个简单的本地服务器三、总结四、问题一、基础知识 概念 网络编程中客户端和服务器指的是进程,而不是常提到的机器或者主机。注意三个概念:请求、响应、事务。 网络编程中客户端-服务器事务是指客户端和…

刷题day65:分割等和子集

题意描述&#xff1a; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 思路&#xff1a; 使用01背包&#xff0c; 背包的体积为sum / 2背包要放入的商品&#xff08;集合里的元素&#xff09;…

linux数据校验

文件 一般对于文件的校验使用md5&#xff0c;centos7系统有自带的md5校验工具md5sum&#xff0c;可以用来校验两个文件是否一致 可以对比一下md5值是否一致来校验文件是否一致 目录 1 若是在主机上使用网络磁盘挂载备份的可以使用diff工具对比两个目录是否一致 diff -r /op…

Nginx使用教程

目录 一、Nginx介绍二、下载和安装三、Nginx命令1.查看版本2.检查配置文件正确性3.启动和停止4.重新加载配置文件 四、配置文件结构五、Nginx具体应用1.部署静态资源2.反向代理3.负载均衡 一、Nginx介绍 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件( IMAP/POP3)代…

少儿编程 中国电子学会图形化编程等级考试Scratch编程二级真题解析(选择题)2023年3月

2023年3月scratch编程等级考试二级真题 选择题(共25题,每题2分,共50分) 1、小猫的程序如图所示,积木块的颜色与球的颜色一致。点击绿旗执行程序后,下列说法正确的是 A、小猫一直在左右移动,嘴里一直说着“抓到了”。 B、小猫会碰到球,然后停止。 C、小猫一直在左右…

《LKD3粗读笔记》(13)虚拟文件系统

《LKD3粗读笔记》(13)虚拟文件系统 虚拟文件系统&#xff0c;简称VFS&#xff0c;是内核的子系统&#xff0c;为用户空间程序提供了文件系统相关的接口。系统中所有文件系统不但依赖VFS共存&#xff0c;而且也依靠VFS系统协同工作。通过虚拟文件系统&#xff0c;程序可以利用标…

文本三剑客正则表达式2

文章目录 文本三剑客&正则表达式21 sed2 sed命令的常用选项3 sed命令的操作符4 打印4.1 按照行号寻址打印4.1.1 只打印第二行4.1.2 只显示行号4.1.3 显示行号及内容4.1.4 只打印最后一行 4.2 进行行号范围区间的打印4.2.1 打印1-3行4.2.2 打印第二行到最后一行4.2.3 打印2-…

操作符续(整型提升与算术转换)

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《C知识系统分享》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c…

MySQL深入浅出: order by if()与order by in()之条件排序

目录 1&#xff1a;原表数据 2&#xff1a;order by if&#xff08;&#xff09; 3&#xff1a;order by in&#xff08;&#xff09; 4&#xff1a;社区地址 1&#xff1a;原表数据 2&#xff1a;order by if&#xff08;&#xff09; SELECT * FROM people ORDER BY IF(…

mysql数据库的表的增删查改

目录 表的增删查改 6.1&#xff1a;增加 6.2&#xff1a;查找 6.3&#xff1a;更新 6.4&#xff1a;删除 6.5&#xff1a; 插入查询结果 6.6&#xff1a;聚合函数 6.7&#xff1a;group by分组 关键字的先后顺序&#xff1a;from > on> join > where > gro…

C语言实现三子棋小游戏

目录 游戏介绍 游戏菜单的创建&#xff08;menu&#xff09; 游戏核心功能实现 棋盘的初始化&#xff08;InitBoard&#xff09; 棋盘的展现&#xff08;printfboard&#xff09; 玩家下棋&#xff08;playerBoard&#xff09; 电脑下棋&#xff08;computerBoard&#…

探究C++构造函数及其优化

目录 一、 类的六个默认成员函数1.1 框架图1.2 具体介绍&#xff08;1&#xff09;构造函数&#xff08;2&#xff09;析构函数&#xff08;3&#xff09;拷贝构造函数&#xff08;4&#xff09;赋值运算符重载函数 归纳我们不写&#xff0c;编译器默认生成了什么&#xff1a; …

2023宁波市赛 天一永安杯赛前模拟题部分wp

Web pop 进hint.php 伪协议读index.php <?php class Tiger{public $string;protected $var;// 恶意参数public function __construct($var){$this->var $var;}public function __toString(){return $this->string;}public function boss($value){// 0eval($valu…

自动化测试作为软件测试的一种技术手段,时常被大家讨论

自动化测试作为软件测试的一种技术手段&#xff0c;时常被大家讨论。本人在自动化技术方面有过略有小成&#xff0c;今天聊一聊关于自动化的一些误区&#xff0c;以帮助新手能正确的了解和认识自动化一些概念。 测试的行为本质是什么&#xff1f; 为什么先从这个概念开始谈起&…

OpenGL之创建窗口

目录 什么是OpenGL&#xff1f; 核心模式与立即渲染模式 立即渲染模式 (Immediate mode) 核心模式(Core-profile) 状态机 对象 创建窗口 配置环境 什么是OpenGL&#xff1f; 一般它被认为是一个API(Application Programming Interface, 应用程序编程接口)&#xff0c;…

黑客最常用的10款黑客工具

以下所有这些工具都是捆绑在一起的Linux发行版&#xff0c;如Kali Linux或BackBox&#xff0c;所以我们一定会建议您安装一个合适的Linux黑客系统&#xff0c;使您的生活更轻松 - 尤其是因为这些黑客工具可以&#xff08;自动&#xff09;更新。 1、Nikto&#xff08;网站漏洞…