首页 > 其他分享 >操作符详解

操作符详解

时间:2024-09-07 20:54:21浏览次数:9  
标签:11111111 10 进制 int 00000000 详解 操作符 printf

一 ,操作符的分类

1.算术操作符:+ ,-, * ,/ ,%

2.移位操作符:<<     >>

3.位操作符    : &   |     ^   ~

4.赋值操作符:  =  ,+= ,-= ,*= ,/= ,%= ,<<= , >>= ,&= ,|= ,^=

5.单目操作符:! , ++ , --,  -  ,  +  , * ,  &  ,  ~  ,sizeof(类型)

6.关系操作符:>  , <  , >=  ,<=  , ==  , !=

7.逻辑操作符:&&    ||   !

8.条件操作符:?   : 

9.逗号表达式:  ,

10.下标引用 : [  ]

11.函数调用 : (   )

12.结构成员访问:  .     ,  -> 

二 ,二级制和进制转化

 在日常生活中,我们常常听到不同的进制,时钟有60进制,12进制,每周的7进制。其实无论是多少进制,都是数值的不同表示方法:

比方说:数值15的各种进制表现形式:

15的2进制:1111

15的8进制:17

15的10进制:15

15的16进制:F

// 16 进制的数值之前写: 0x

//  8进制的数值之前写   :0

以我们日常生活中常见的10进制做类比:

  • 10进制满10进1
  • 10进制的每一位数值是0-9组成的

推理到2进制:

  • 2进制满2进1
  • 2进制的每一位数值都是0-1组成的 

 2.1 二进制转十进制

在10进制中,520表示的是五百二十,是因为10进制中的每一位数都是有权重的,这里的520=0*10^0  +  2*10^1  + 5*10^2;

这与二进制也是类似的,只不过权重不同:

这里以二进制1101来举例:

 

2.2 10进制转二进制 

2.3 2进制转8进制

8进制的数字每一位都是0-7的,然后0-7的数字写成2进制,最多需要3位二进制数就足够了,比方说7的二进制数是111,所以在2进制数转成8进制数的时候,从最右边的最低为开始选3个数为一组来计算即可,如果不够3位,前补0或者直接换算; 

 2.4 2进制数转16进制数

16进制的每一位是0-9,a-f,然后把它们写成二进制的数,最多需要4位二进制数即可;

注意16进制表示时,前面加0x,所以二进制数 01101011 的16进制形式为  0x6b;

三 ,原码,补码,反码

 整数中的2进制表示方法有三种,即原码,反码,补码

有符号整数的三种表示方法均有符号位和数值为两部分,2进制序列中,最高位的1位被当作符号位,其余的都是数值为。

符号位中以0表示“正”,以1表示”负“;

 

四 , 移位操作符

<<      左移操作符

>>      右移操作符

注意: 操作数只能是整数

 4.1 左移操作符

移位规制:左边抛弃,右边补0  ---->进行运算的是数值的二进制(内存中的补码)

正数: 

int main()
{
	int n = 10;
	int m = n << 1;
	printf("%d\n", n);
	printf("%d\n", m);
	return 0;
}

 

负数:

int main()
{
	int a = -10;
	int b = a << 1;
	int c = a << 2;
	//10000000 00000000 00000000 00001010
	//补码:
	//11111111 11111111 11111111 11110110
	//左移:
	//11111111 11111111 11111111 11101100 --- b在内存中的补码
	//10000000 00000000 00000000 00010100
	printf("%d\n", a);
	printf("%d\n", b);
	printf("%d\n", c);
	return 0;
}

我们不难发现:左移运算符在一定程度上(并不是都是),每次左移一位,数值会在原有的基础上乘以2; 

需要注意的是:负数要转化成补码进行移动,最后在转化原码得到移动结果。

 

4.2 右移操作符

移动规则: 右移运算分为两种

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

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

右移是逻辑右移还是算数右移是取决于编译器的,但大多数的编译器采用的是算数右移。 

 

int main()
{
	int a = -10;
	int b = a >> 1;
	int c = a >> 2;
	int d = a >> 3;
	//10000000 00000000 00000000 00001010
	//11111111 11111111 11111111 11110110  --- 补码
	//11111111 11111111 11111111 11111011  
	//10000000 00000000 00000000 00000101 -- -5
	//右移到底是算数右移还是逻辑右移,是取决于编译器,但是大部分编译其采用的都是算术右移
	printf("%d\n", a);
	printf("%d\n", b);
	printf("%d\n", c);
	printf("%d\n", d);

	return 0;
}

 注意:对于移位符号位,不要移动负数位,这个标准是未定义的。

五 ,位操作符

&      按位与

 |       按位或

 ^      按位异或

 ~      按位非

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

int main()
{
	int a = -5;
	int b = 13;
	int c = a & b;
	int d = a | b;
	int e = a ^ b;
	int f = ~a;

	//a:10000000 00000000 00000000 00000101
	//a:11111111 11111111 11111111 11111011  -5补码
	//b:00000000 00000000 00000000 00001101  13补码
	//  00000000 00000000 00000000 00001001    & 9
	//  11111111 11111111 11111111 11111111    | 
	//  10000000 00000000 00000000 00000001
	//  11111111 11111111 11111111 11110110     ^
	//  10000000 00000000 00000000 00001010
	//  00000000 00000000 00000000 00000100     ~
	printf("%d\n", c);
	printf("%d\n", d);
	printf("%d\n", e);
	printf("%d\n", f);

	return 0;
}

 试着完成:

题目:不能创建临时变量(第三个变量),从而实现两个整数交换;

如果我们使用第三个变量就很容易实现,就像现在你有一瓶酱油和一瓶醋,我们可以通过一个空瓶子把这两个容器里的液体交换;

int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	printf("交换前:%d %d\n", a, b);
	c = a;
	a = b;
	b = c;
	printf("交换后:%d %d\n", a, b);
	return 0;
}

但是题目要求不可以用第三个变量,但我们可以先把两个数相加,然后赋值给a,然后再用赋值后的a减去b  实则上就是我们原来的a值,然后把这个值赋给 b ,再用a值减去 b ,就是我们原来的 b 值,然后赋值给a ,即可实现交换;

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:%d %d\n", a, b);
	a = a + b;
	b = a - b;
	a = a - b;
	printf("交换后:%d %d\n", a, b);
	return 0;
}

 这样计算,当数值较小的时候的确可以实现我们的数值交换,但是当数值越来越大的时候,两个数值进位相加,可能会超出整数表达的最大值,可能会存在溢出问题!

所以我们再次优化这个算法:

a^a   == 0

a^0   ==a 

异或:相异为1,相同为0

两个二进制数没有进位运算,不会导致溢出!

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d b=%d\n", a, b);
	a = a ^ b;
	b = a ^ b;  //a ^ b^ b; ----> a
	a = a ^ b; //a ^ b ^ a ---->b
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

 练习1:

编写代码实现:求⼀个整数存储在内存中的⼆进制中1的个数; 思路:在10进制中,我们 %10取余,得到某一个数值的最后一位数; /10取整得到某一个数值去掉最后一位数的数值;
int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		if (n % 2 == 1) {
			count++;
		}
		n /= 2;
	}
	printf("二进制中1的个数为%d\n", count);
	return 0;
}

但是我们很快就可以发现,针对负数是有问题的;

然后我们,就会想,什么运算会不丢失原有而二进制的数值,然后又可以方便计算的,最后,我们可以想到按位与(&),让每一个二进制数按位与1

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((1 & (n >> i)) == 1)
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

 但是这里我们计算了32次,然后我们思考,能不能再优化一下:

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("%d\n", count);
	return 0;
}

 练习二:

题目:写一个代码判断 n 是否是2的次方数

2次方数有:1   2   4   8   16   32   64   .....

我们把他们转成二进制数:

00000001

00000010

00000100

00001000

00010000

................

我们发现他们有且仅有一个1 

int main()
{
	int n = 0;
	scanf("%d", &n);
	if ((n & (n - 1)) == 0)
	{
		printf("Yes!\n");
	}
	else
	{
		printf("No!\n");
	}
	return 0;
}

 案例3:

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

13 的 2 进制序列: 00000000000000000000000000001101 将第 5 位置为 1 后: 00000000000000000000000000011101 将第 5 位再置为 0 : 00000000000000000000000000001101
int main()
{
	int n = 13;
	n |= (1 << (5 - 1));
	//00000000 00000000 00000000 00001101
	printf("%d\n", n);

	//00000000 00000000 00000000 00001101   --13
	//00000000 00000000 00000000 00010000    |
	// 1 <<4
	//00000000 00000000 00000000 00011101
	//11111111 11111111 11111111 11101111    &
	//00000000 00000000 00000000 00001101
	n &= (~(1 << (5 - 1)));
	printf("%d\n", n);

	return 0;
}

 

六 ,单目操作符

 !、++、--、&、*、+、-、~ 、sizeof、(类型)

 

七 , 逗号表达式

1.以逗号依次隔开的多个表达式

2.从左向右依次执行,整个表达式的结果是最后一个表达式的结果

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
//代码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)
{
 //业务处理
}

 

八 , 下标访问[],函数调用()

 1.下标引用操作符[]:  操作数:一个数组名+一个索引值

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

2.函数调用():

# include <stdio.h> void test1 () { printf ( "hehe\n" ); } void test2 ( const char *str) { printf ( "%s\n" , str); } int main () { test1(); // 这⾥的 () 就是作为函数调⽤操作符。 test2( "hello bit." ); // 这⾥的 () 就是函数调⽤操作符。 return 0 ; }

九,操作符的属性:优先级,结合性

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

优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。 如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符 是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右 结合(从右到左执⾏)

 

十, 表达式求值

1.整型提升:

C语⾔中整型算术运算总是⾄少以缺省(默认)整型类型的精度来进⾏的。为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升
int main()
{
	char a = 5;
	//00000000 00000000 00000000 00000101
	//00000101   截断
	char b = 126;
	//00000000 00000000 00000000 01111110
	//01111110
	char c = a + b;
	//00000101   --- 发生整型提升  --00000000 00000000 00000000 00000101
	//01111110   --- 发生整型提升  --00000000 00000000 00000000 01111110
	//char  --- signed char 
	//00000000 00000000 00000000 10000011  --- 截断 -- 10000011
	//当以%d 形式进行打印的时侯,打印的是有符号整数
	//又会对c发生整形提升
	//11111111 11111111 11111111 10000011 -- 内存中的补码
	//10000000 00000000 00000000 01111101
	printf("%d\n", c);
	return 0;
}

如何进⾏整体提升呢?

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

2.算数转换:

如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数的转换为另⼀个操作数的类型,否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换
long double double float unsigned long int long int unsigned int int
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算

 

标签:11111111,10,进制,int,00000000,详解,操作符,printf
From: https://blog.csdn.net/khjjjgd/article/details/141988669

相关文章

  • Shopify主题目录结构详解
    Shopify主题控制在线商店的组织、功能和风格。主题代码使用特定于Shopify主题的文件的标准目录结构组成。本文主要是对Shopify主题目录的详细解析:一、主要目录及功能1、layout目录功能:存放主题的布局文件,如theme.liquid。这些文件定义了商店页面的基本结构和布局,包括头部......
  • Vue3技术分享专栏 - Composition API详解
    引言在上一篇文章中,我们通过一个简单的“HelloWorld”示例介绍了如何使用Vue3和CompositionAPI来创建响应式的组件状态。本文将更深入地探讨CompositionAPI,解释其背后的原理,并提供一些实际的应用案例。CompositionAPI概述CompositionAPI是Vue3中的一个新特性,它为开......
  • mybatis事务详解
    对于数据库事务,我们都不陌生,数据库的事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,也是一个不可分割的工作单位。它包含一个或多个SQL语句,这些语句要么全部执行,要么全部不执行。事务是一个原子操作单元,其对数据的修改要么全都执行,要么全都不执行。那么我们就得来看看这......
  • g++如何判断>>是模板结束还是右移操作符
    intro在使用模板声明中,有一个经典的问题就是如何区分模板声明中的">>"是右移操作符还是一个模板声明的结束标志。好在新的C++标准削弱了这个很强的限制,而是允许reasonable的、根据上下文对符号进行不同的解析。C++11improvesthespecificationoftheparsersothatmultipl......
  • Java IO流详解:像流水一样读写数据
    JavaIO流详解:像流水一样读写数据在Java编程世界中,IO流就像水流一样,不断地在内存和外部存储之间搬运数据。这些数据流可以是字节,也可以是字符。不管是文件读写、网络传输,还是数据处理,IO流总是无处不在。下面我们就来揭开IO流的面纱,看看它是如何工作的,以及在实际开发中有哪些应用......
  • MySQL的安装4000字详解(小白也能看得懂!!!!)
    MySQL的安装与使用2.1.1MySQL发展历史和版本2.2.1MySQL的安装与配置1.下载MySQL安装文件2.选择下载版本3.安装配置MySQL4.选择安装路径,推荐换到D盘!!!!!5.执行安装6.完成安装,点击next7.配置8.设置账户9.配置服务器10.开始执行11设置router配置,默认即可12.输入密码,检查是否......
  • 关于ST-CNN的算法详解
    ST-CNN(时空卷积神经网络)是一种结合了时间和空间维度信息处理的深度学习模型,它在多个领域,如交通流量预测、视频分析、动作识别等中都有广泛应用。以下是对ST-CNN算法的详细解析:一、基本概念ST-CNN通过结合卷积神经网络(CNN)和图卷积网络(GCN)的优势,能够同时捕捉数据的空间和时间特......
  • 关于卷积神经网络(CNN)的详解
    一、基本概念卷积神经网络(ConvolutionalNeuralNetworks,CNN)是一类包含卷积计算且具有深度结构的前馈神经网络(FeedforwardNeuralNetworks,FNN),是深度学习的代表算法之一。它仿造生物的视觉机制构建,能够进行监督学习和非监督学习,广泛应用于图像识别、视频分析、自然语言处......
  • MySQL 性能压测工具-sysbench使用详解
    sysbench是一个开源的、基于LuaJIT(LuaJIT是Lua的即时编译器,可将代码直接翻译成机器码,性能比原生lua要高) 的、可自定义脚本的多线程基准测试工具,也是目前用得最多的MySQL性能压测工具。基于sysbench,我们可以对比MySQL在不同版本、不同硬件配置、不同参数(操作系统和数据......
  • SQL中的事务、索引、视图、游标、触发器、存储过程概念详解
    SQL中的事务、索引、视图、游标、触发器、存储过程概念详解前几天面试的时候,面试官突然问了句“怎么解释SQL的事务?”,太久没接触了,突然就答不上来这种基础的问题了,好丢捻。于是今天打算整理一下基础概念,发在博客里时刻提醒自己。一、事务想象你正在超市购物。事务就好比你从挑......