学习笔记5:关于操作符与表达式的求值

news2024/12/26 11:48:46

目录​​​​​​​

一.移位操作符

1.左移操作符

2.右移操作符

二.位操作符

1.位运算基本知识

2.位运算的巧妙运用

 三.其他操作符

1.算术操作符

2.单目操作符

3.关于逻辑操作符

 四.表达式求值

隐式类型转换

(1)整形提升(短整型家族数据的二进制序列补位转换)

(2).算术转换


一.移位操作符

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

1.左移操作符

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

2.右移操作符

移位规则:
首先右移运算分两种:
(1). 逻辑移位
左边用0填充,右边丢弃
(2). 算术移位
左边用原该值的符号位填充,右边丢弃

在vs2022编译器中,整形数据的右移操作执行的是算术右移,即移动后,左边的二进制位用原数据的符号位来填充。

二.位操作符

1.位运算基本知识

&
|
^
按位与
按位或
按位异或

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

按位与&:两个整数对应的二进制位如果同为1则该位的运算结果为1,否则为0

按位或|两个整数对应的二进制位只要有一个1则该位的运算结果为1,否则为0

按位异或^:两个整数对应的二进制位不同则该位的运算结果为1,否则为0

位运算按位与,按位或,按位异或都满足交换律和结合律。位运算的交换律和结合律有十分巧妙的运用。

2.位运算的巧妙运用

leetcode645. 错误的集合问题描述:

leetcode链接:645. 错误的集合 - 力扣(Leetcode)

集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合丢失了一个数字 并且 有一个数字重复 。

给定一个数组 nums 代表了集合 S 发生错误后的结果。

请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

题解函数接口定义:int* findErrorNums(int* nums, int numsSize, int* returnSize)

nums是给定的数组的首地址

numsSize是数组的元素个数

returnSize是记录返回的数组(需要动态内存分配函数来开辟)的元素个数的变量的地址

注意给定的数组是乱序的

本题的其中的一个求解办法就是用位运算:

第一步:利用按位异或运算的可交换性和可结合性可以得到丢失的数字和重复的数字两个数字的按位异或的结果。

我们将题目给定的错误的数组和正确的原数组两个数组中的所有元素进行按位异或的运算。

	int Norsum = 0;
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		Norsum ^= nums[i];
		Norsum ^= (i + 1);
	}

Norsum中便记录了数组中重复的数字和丢失的数字按位异或的结果

Norsum的二进制序列相当于记录了重复的数字和丢失的数字的二进制序列的不同位

第二步:

取出Norsum中的最低位的1记录在lowbit变量中:

	int Norsum = 0;
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		Norsum ^= nums[i];
		Norsum ^= (i + 1);
	}
	int lowbit = Norsum &(-Norsum);

此时lowbit就记录了重复的数字和丢失的数字两个正整数二进制序列最低的不同位。

第三步:将题目给定的数组和正确的原数组的所有元素逐一与lowbit进行按位&运算,由于lowbit的二进制序列只有一位为1,所以每次按位与运算的结果要么为0要么等于lowbit。

 

	int Norsum = 0;
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		Norsum ^= nums[i];
		Norsum ^= (i + 1);
	}
	int lowbit = Norsum &(-Norsum);
	int x = 0;
	int y = 0;
	for (i = 0; i < numsSize; i++)
	{
		if (0 == lowbit & nums[i])
		{
			x ^= nums[i];
		}
		else
		{
			y ^= nums[i];
		}
	}
	for (i = 0; i < numsSize; i++)
	{
		if (0 == lowbit & (i + 1))
		{
			x ^= (i + 1);
		}
		else
		{
			y ^= (i + 1);
		}
	}

两组元素分别按位异或后结果存放在x和y两个变量中,由于相同的元素必然分到同一组,重复的元素和丢失的元素必然被分到不同组所以最终x和y分别为丢失的元素和重复的元素的其中一个,但是无法确定x,y与丢失元素和重复元素的具体对应关系,最后只需再遍历一次nums数组确定这个对应关系即可。

	int* Return = NULL;
	if ((Return = (int*)malloc(sizeof(int) * 2)) == NULL)
	{
		printf("malloc failed\n");
        exit(0); 
	}

开辟一个两个元素的数组来存储结果:Return[0]存放重复的元素

                                                           Return[1]存放丢失的元素

再遍历一次nums数组确定x和y与丢失的元素和重复的元素的对应关系。

	int flag = 1;                       用flag来标记x是否为重复的元素
	for (i = 0; i < numsSize; i++)      确定x是否存在于nums数组中。
	{
		if (x == nums[i])
		{
			Return[0] = x;
			Return[1] = y;
			flag = 0;
		}
	}
	if (flag)
	{
		Return[0] = y;
		Return[1] = x;
	}

题解代码:

int* findErrorNums(int* nums, int numsSize, int* returnSize)
{
	int Norsum = 0;
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		Norsum ^= nums[i];
		Norsum ^= (i + 1);
	}
	int lowbit = Norsum &(-Norsum);
	int x = 0;
	int y = 0;
    利用lowbit将两组元素分成两组分别以按位异或的方式存入x和y中。                                
	for (i = 0; i < numsSize; i++) 
	{
		if (0 == (lowbit & nums[i]))
		{
			x ^= nums[i];
		}
		else
		{
			y ^= nums[i];
		}
	}
	for (i = 0; i < numsSize; i++)
	{
		if (0 == (lowbit & (i + 1)))
		{
			x ^= (i + 1);
		}
		else
		{
			y ^= (i + 1);
		}
	}
	int* Return = NULL;
	if ((Return = (int*)malloc(sizeof(int) * 2)) == NULL)
	{
		printf("malloc failed\n");
		exit(0);
	}

	int flag = 1;                       用flag来标记x是否为重复的元素
	for (i = 0; i < numsSize; i++)      确定x是否存在于nums数组中。
	{
		if (x == nums[i])
		{
			Return[0] = x;
			Return[1] = y;
			flag = 0;
		}
	}
	if (flag)
	{
		Return[0] = y;
		Return[1] = x;
	}
	*returnSize = 2;
	return Return;
}

 三.其他操作符

1.算术操作符

+        -     *       /       %
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

2.单目操作符

!
-
+
&
sizeof
~
--
++

*

(类型)

逻辑反操作
负值
正值
取地址
操作数的类型长度(以字节为单位)
对一个数的二进制按位取反
前置、后置--
前置、后置++

间接访问操作符(解引用操作符)

强制类型转换

注意sizeof(类型名)时括号不能省

3.关于逻辑操作符

#include <stdio.h>
int main()
{
        int i = 0,a=0,b=2,c =3,d=4;
        i = a++ && ++b && d++;
        printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
        return 0;
}
程序输出的结果是什么?

逻辑操作符尤其要注意的是逻辑短路规则:

比如:

对于逻辑与 &&(且)表达式:a&&b,若表达式a为假(值为0),则计算机不会计算表达式b,整个表达式的结果为0;

对于逻辑或 ||   (或)表达式:a||b   ,若表达式a为真(值为非0),则计算机不会计算表达式b,整个表达式结果为1;

代码段中的表达式i = a++ && ++b && d++;

由于a++是先访问a的值再完成a的自增,所以a++表达式的值为0,基于逻辑短路规则,&&后面的逻辑表达式不会再计算,i的值被赋为0,a自增为1,b,d的值不变,因此打印的结果为a=1,b=2,c=3,d=4;

 四.表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

只要有表达式,我们就要考虑类型转换的问题。

隐式类型转换

(1)整形提升(短整型家族数据的二进制序列补位转换)

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

为了获得这个精度,表达式中的字符和短整型等(字节数小于整形int)操作数在使用之前被转换为普通整型(二进制序列补到与int同位数),这种转换称为整型提升。

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

对于有符号的短整型家族数据,整形提升时二进制序列高位补符号位。

对于无符号的短整型家族数据,整形提升时二进制序列高位补0。

相关实例: 

阅读代码预测输出结果:

int main()
{
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;
    if(a==0xb6)
    printf("a");
    if(b==0xb600)
    printf("b");
    if(c==0xb6000000)
    printf("c");
    return 0;
}

0xb6和0xb600都是整形数据存入a和b都会发生截断

0xb6对应的二进制序列为:        00000000000000000000000010110110

0xb600对应的二进制序列为:    00000000000000001011011000000000

0xb6截断后存入a中的序列:    10110110(最高位被视为符号位)

0xb600截断后存入b中的序列:1011011000000000(最高位被视为符号位)

a==0xb6比较时a发生整形提升高位补1,结果不再相同.

b==0xb600比较时a发生整形提升高位补1,结果不再相同.

所以最后只打印c

相关实例: 

阅读代码预测输出结果:

int main()
{
    char c = 1;
    printf("%u\n", sizeof(c));
    printf("%u\n", sizeof(+c));
    printf("%u\n", sizeof(-c));
    return 0;
}

+c和-c都是运算表达式.

因此sizeof(+c),sizeof(-c)中c会发生整形提升算出来结果为4个字节。(VS2022整形为4个字节)。

相关实例: 

阅读代码预测输出结果:

int main()
{
  unsigned char a = 200;
  unsigned char b = 100;
  unsigned char c = 0;
  c = a + b;
  printf(“%d %d”, a+b,c);
  return 0;
}

200截断存入a中的二进制序列为:1100 1000

100截断存入b中的二进制序列为:0110 0100

c= a+b 运算时a和b都要发生整形提升,由于是无符号数所以高位补0。

a+b整形提升后运算结果为:00000000000000000000000100101100

截断后存入c中的二进制序列:      00101100(转换为十进制为44)

a+b和c打印前a,b,c也要发生整形提升。

最后打印结果为:300 44

(2).算术转换

且某个操作符的各个操作数的大小如果都大于或等于整形,且各个操作数属于不同的类型,那么除非其中一些操作数的转换为另一个操作数的类型,否则操作就无法进行。这种转换称为算术转换。

算术转换的原则是小的,精度低的数据类型向更大,精度更高的数据转换(向上转换原则)。

相关实例: 

阅读代码预测输出结果:

int main()
{
    int i;
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0;
}

sizeof()操作符返回的结果为无符号整形(unsigned int) .

注意i为全局变量,会默认初始化为0.

所以i和sizeof(i)比较时i会发生算术转换,转换为无符号数,而i原本为-1,二进制序列为三十二个1,所以转换为无符号数后是一个非常大的正数。所以程序会输出>

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

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

相关文章

【最新】SpringBoot集成Dubbo3

最近在学习dubbo&#xff0c;构建一个简单的springboot集成dubbo&#xff0c;中间也是出了好多问题&#xff0c;在这记录下整体的过程。 1. 构建SpringBoot环境 一个简单的聚合工程 dubbo-consumer&#xff1a;是服务消费方dubbo-provider&#xff1a;是服务提供方dubbo-inte…

机器学习笔记之前馈神经网络(二)非线性问题

机器学习笔记之前馈神经网络——非线性问题引言回顾&#xff1a;关于非线性问题解决非线性问题的三种方式引言 上一节介绍了从机器学习到深度学习的过渡&#xff0c;并介绍了深度学习的发展过程。本节将主要介绍如何使用神经网络处理非线性问题 回顾&#xff1a;关于非线性问…

决策树生成、决策树可视化、决策树算法api、泰坦尼克号乘客生存预测案例代码

一、决策树算法api class sklearn.tree.DecisionTreeClassifier(criterion’gini’,max_depthNone,random_stateNone) criterion&#xff1a;特征选择标准&#xff0c;"gini"或者"entropy"&#xff0c;前者代表基尼系数&#xff0c;后者代表信息增益&…

来自 GitHub 2022 的趋势和见解

《Github 2022 发展趋势和见解》发布了这件事小伙伴们知道了吧&#xff1f;这是每个程序员不能错过的年度报告&#xff0c;因为里面详细介绍了语言的发展趋势和热门领域的介绍。那就让我们来看看吧 目录 编程语言 地理分布 贡献时间分配 技术发展趋势 最受欢迎的存储库 …

GoogLeNet详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、GoogLeNet…

C++入门——auto、范围for、nullptr

下一篇就要类和对象了&#xff0c;剩了点零碎的知识点就浅浅水一篇把 一. auto关键字 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;但遗憾的 是一直没有人去使用它&#xff0c;这是由于变量本身就具备生命周期…

算法及时间、空间复杂度

算法 算法是对问题求解过程的一种描述&#xff0c;是为解决一个或一类问题给出的一个确定的、有限长的操作序列。严格说来&#xff0c;一个算法必须满足以下5个重要特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;对于任意一组合法的输入值&#xff0c;在执行有…

【数据结构与算法——C语言版】5. 排序算法(2)——冒泡排序

前言 上篇文章【数据结构与算法——C语言版】4. 排序算法&#xff08;1&#xff09;——选择排序我们介绍了排序算法中的选择排序&#xff0c;其时间复杂度是O(n2)&#xff0c;本篇文章我们将介绍另一种同样时间复杂度是O(n2)的排序算法——冒牌排序&#xff0c;这两种算法思路…

ChatGPT背后的开源AI框架Ray,现在值10亿美元

Ray 被 OpenAI、亚马逊等科技公司用来开发大模型&#xff0c;是最近异军突起的框架。 最近一段时间&#xff0c;文本生成的人工智能在互联网上掀起了一阵风暴&#xff1a;ChatGPT 因为可以对人们能想到的几乎任何问题提供非常详细、近乎逼真的回答而受到追捧。大模型应用的出现…

Mapper代理开发案例及MyBatis核心

本片文章需要参考我的前一篇文章&#xff1a;MyBatis入门案例引入总结&#xff0c;使用mapper代理开发的好处就是可以解决开发中硬编码的问题和简化后期的SQL执行。使用这种方式可以不用写接口的实现类&#xff0c;免除了复杂的方法&#xff0c;使得代码更加清晰易懂按照以前的…

vue的过渡动画(有vue的动画库和ui库的介绍)

一、概念 Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。 二、默认过渡 <template><div><button click"isShow!isShow">显示/隐藏</button><transition appear><h1 v-show"isShow" cl…

过滤器和拦截器的使用及管理

参考&#xff1a;(70条消息) Spring过滤器和拦截器的区别_yjc0403的博客-CSDN博客https://www.cnblogs.com/colin220/p/9606412.htm概述过滤器&#xff1a;是在javaweb中&#xff0c;你传入的request、response提前过滤掉一些信息&#xff0c;或者提前设置一些参数&#xff0c;…

Anaconda安装之后Spyder打不开解决办法--目前有用 jupyter notebook 无法正常运行2023.1.7

纯纯小白&#xff0c;探索一天&#xff0c;终于成功&#xff0c;需要我的经历没有白费&#xff0c;让大家少走弯路。 问题描述 从官网下载Anaconda之后&#xff0c;安装&#xff0c;一切正常。打开Anaconda navigator在弹出窗口选择了更新&#xff08;我怀疑这就根源&#xf…

Js逆向教程24-作用域和自执行函数

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; Js逆向教程24-作用域和自执行函数 一、变量作用域 1.1局部变量 function jb() {var a"我是局部变量"return a; }1.2全局变…

【Java寒假打卡】Java基础-异常

【Java寒假打卡】Java基础-异常异常概述throws声明异常throw抛出异常try-catch 抛出异常throwable的成员方法异常概述 Exception:称之为异常类&#xff0c;他表示程序本身可以处理的问题 RuntimeException及其子类&#xff1a;运行时异常。&#xff08;空指针异常&#xff0c;…

JUC总结系列篇 (二) : 对线程的理解和使用总结

文章内容&#xff1a; 一.为什么需要多线程 二.线程的创建 三.线程的方法sleep(),run(),wait(),yeid(),join(),interrupt()等方法归纳总结 四.线程的状态及其转换 五.线程的交替执行案例 六.多个线程依次执行案例 七.多线程并发带来的线程安全问题 一.为什么需要多线程&#x…

Linux项目自动化构建工具-make/Makefile

一、前言 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力。一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件…

前端入门笔记 04 —— Web(html CSS)布局

响应式布局 屏幕尺寸变化&#xff0c;需要响应式网页设计RWD web页面适应不同屏幕宽度因素 液态站点&#xff0c;拉伸充满浏览器窗口 小屏幕挤成一团&#xff0c;大屏幕空白间隙过大固定宽度 像素为单位固定尺寸 小屏幕滚动&#xff0c;大屏幕空白 实现 设置meta标签媒体查…

数据结构入门5-1(数和二叉树)

目录 注 树和二叉树的定义 树的定义 树的基本术语 二叉树的定义 树和二叉树的抽象数据类型定义 二叉树的性质和存储结构 二叉树的性质 二叉树的存储结构 1. 顺序存储结构 2. 链式存储结构 遍历二叉树和线索二叉树 遍历二叉树&#xff08;traversing binary tree&a…

加密与安全

目录 一、编码算法 1.1、ASCII 1.1.1、ASCII简介 1.1.2、ASCII产生原因 1.1.3、表达方式 1.1.4、标准表 1.1.5、大小规则 1.2、Unicode 1.2.1简介 1.2.2编码和实现 1.3、汉字编码 1.3.1、GB2312-80 标准 1.3.2、GBK 编码标准 1.3.3、GB18030编码标准 1.4、URL编…