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

操作符详解

时间:2023-07-18 22:36:53浏览次数:30  
标签:main int 详解 取整 printf 操作符 include

写在前面

这里的内容虽然有些多,不过整体较为简单,我总结了一些相对有些难度的的知识点。

操作符

C语言的操作符有很种,这里我把常见的一些和大家进行分析一下.

  • 算术操作符
  • 移位操作符
  • 位操作符
  • 赋值操作符

算术操作符

所谓的算数操作符就是我们的加减乘除,没有什么可以谈的.

+ - * / %

我在这里就谈谈 % 这个操作符,这个操作符是我们取余数的操作,有点意思.

int main()
{
	int num = 11;
	int ret = num % 10;
	printf("%d\n", ret);
	return 0;
}

操作符详解_操作符

注意,只有整数可以取余.

int main()
{
	double num = 11.0;
	double ret = num % 10;
	printf("%lf\n", ret);
	return 0;
}

操作符详解_操作符_02

移位操作符

在谈下面几个操作符时,我先说一下数据在内存中的存储形式,我们知道数据可以用三种二进制方式进行表示,其中补码是计算机的存储方式,毕竟我们数据存在负数,关于原反补三个是如何计算的,我们这里不谈.移位操作符分为下面的两种.

  • 左移
  • 右移

左移

注意左移操作符不是向左移动,准确的说应该是向高位移动,只不过我们平常喜欢把高位放在左边,故我们称之为左移.那么请问当我们左移之后,要知道类型的比特位数是固定的,那么最右边的空位我们因该如何做?这空缺位补0,某种意义上移动一位相当于数据翻倍.

操作符详解_取整_03

#include<stdio.h>

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

操作符详解_取整_04

右移

右移是向低位移动.右移分为逻辑右移和算术右移.

  • 逻辑右移 二进制向右移动 空缺位补 0
  • 算术右移 二进制向右移动 空缺位补 符号位

对于正数来说,右移时逻辑右移还是算术右移都一样,但是负数就不一样了,

操作符详解_操作符_05

大多数编译器支持算术右移,下面我们看看VS系列怎么样

#include<stdio.h>

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

操作符详解_操作符_06

位操作符

位操作符能够直接改变数字的二进制,下面我就以两个例题来总结位操作符的用法,注意,他们的操作数必须是整数。

  • 按位与 &
  • 按位或 |
  • 按位异或 ^
  • 按位取反 ~
如何判断一个数是不是2的n次幂,我们看看下面数的特点

操作符详解_取整_07

你会发现,凡是2的n次幂的数 ,他的二进制一定只有一个数字为 1 (这里不考虑负数),那么这就带来一种思路,我们是否可以通过位操作符进行判断,

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
    // n-1 一定会拿掉那个唯一的 1 的
	if ((n & (n - 1)) == 0)
	{
		printf("YES\n");
	}
	return 0;
}

操作符详解_操作符_08

交换a,b,不出现第三个变量,这里直接就给思路了 我们发现 n ^ n ==0 , n ^ 0 == n 看下面代码

#include<stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	printf("Before a = %d b = %d\n", a, b);

	a = a ^ b;
	b = a ^ b;     //把 b 赋给 a
	a = a ^ b;     //把 a 赋给 b
	printf("After  a = %d b = %d\n", a, b);
	return 0;
}

操作符详解_操作符_09

赋值操作符

这里的赋值操作符就是 = 比较简单,这里就提一下 允许连续赋值

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	a = b = 5; //我们不建议 代码风格不好
	printf("%d %d", a, b);
	return  0;
}

操作符优先级

注意,操作符的优先级是会影响我们的我们代码的,如果操作符的优先级记忆错了那么有可能会带来很大的影响.不过我们不谈,与其我们背诵操作符的优先级不如我们多多使用括号,这样逻辑还清晰,而且不容易出错.

取整方式

我们都知道 5 / 2 = 2,这是我们记住的,那么我们想过没有,为什么结果是2呢?有人可能这样想在数学中 5 / 2 = 2.5,结果是要求是整型,去尾,所以是2.那为什么不是四舍五入得到 3 呢?这是编译器不同的取整方式造成的.下面我们谈谈取整的几种方式.

零向取整

零向取整 , 本质是向0靠近,不一定是四舍五入,否则-2应该是-3.

操作符详解_操作符_10

#include<stdio.h>

int main()
{
	int a = 2.9;
	int b = -2.9;

	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

操作符详解_取整_11

这也解决了 为何5 / 2 结果是2的问题,C语言默认采用零向取整.这里有一个函数,支持零向取整.

#include<stdio.h>
#include<math.h>

int main()
{
	printf("%d\n", (int)trunc(2.9));
	printf("%d\n", (int)trunc(-2.9));
	return 0;
}

操作符详解_取整_12

地板取整

所谓的地板取整就是向负无穷取整.

操作符详解_取整_13

如果我想要它像负无穷取整的话.这里也是通过一个函数来实现.

操作符详解_操作符_14

#include <stdio.h>
#include <math.h> //因为使用了floor函数,需要添加该头文件

int main()
{
	//本质是向-∞取整,注意输出格式要不然看不到结果
	printf("%.1f\n", floor(-2.9)); //-3
	printf("%.1f\n", floor(-2.1)); //-3
	printf("%.1f\n", floor(2.9)); //2
	printf("%.1f\n", floor(2.1)); //2

	return 0;
}

操作符详解_取整_15

向+∞取整

这个和上面恰好相反.

操作符详解_操作符_16

相关的函数如下.

操作符详解_操作符_17

#include <stdio.h>
#include <math.h>

int main()
{
	//本质是向+∞取整,注意输出格式要不然看不到结果
	printf("%.1f\n", ceil(-2.9)); //-2
	printf("%.1f\n", ceil(-2.1)); //-2
	printf("%.1f\n", ceil(2.9)); //3
	printf("%.1f\n", ceil(2.1)); //3

	return 0;
}

操作符详解_取整_18

四舍五入取整

我们来到了我们最熟悉的的取整方式了.这里是round函数.

#include <stdio.h>
#include <math.h>

int main()
{
	//本质是四舍五入
	printf("%.1f\n", round(2.1));
	printf("%.1f\n", round(2.9));
	printf("%.1f\n", round(-2.1));
	printf("%.1f\n", round(-2.9));

	return 0;
}

操作符详解_取整_19

取模

下面我们说一下是什么是取模?

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r 且0 ≤ r < d.其中,q被称为商,r 被称为余数.

正数取模

这里很简单,我们一眼就可以看出了,重点放在负数上面,他们的一些总结是可以用在这里的.

int main()
{
	int a = 10;
	int d = 3;
	printf("%d\n", a % d);
	return 0;
}

操作符详解_取整_20

负数取模

这里才是我们的大头,我们试试不同的编译环境然后仔细分析一下我们该如何做.

#include <stdio.h>
#include <math.h>

int main()
{
	int a = -10;
	int d = 3;
	//printf("%d\n", a/d); //C语言中是-3,很好理解
	printf("%d\n", a % d);
	
	return 0;
}

我们把这个代码在不同的环境下跑一遍,先看现象,再来和大家分析.

操作符详解_操作符_21

我们不是说余数大于0吗?是不是定义错了.实际上因为在C语言中,现在-10%3出现了负数,根据定义:满足 a = q\*d + r 且0 ≤ r < d,C语言中的余数,是不满足定义的,因为,r<0了.故,大家对取模有了一个修订版的定义:

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r , q 为整数,且0 ≤ |r|< |d|.其中,q 被称为商,r 被称为余数.

那么在Python中呢?这里的结果是什么样的呢?

操作符详解_操作符_22

这里我们就疑惑,好像出现了矛盾,实际上不是的,这是我们不同语言默认的取整的方式不同, Python采用向-∞取整

操作符详解_取整_23

这里我们给出一个结论,方式决定商,商决定余数

下面我们继续说一下,我们有的时候把取模也称之为取余,这里还是有一定的区别的.

  • 取余:尽可能让商,进行向0取整.
  • 取模:尽可能让商,向-∞方向取整

提升与截断

我们之前谈到变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。可是我们看一下下面的代码.

int main()
{
	int a = 10;
	char ch = 'a';
	int x = ch;   // 疑问1
	ch = a;       // 疑问2
	return 0;
}

操作符详解_操作符_24

这里面我们有两个疑问,我们之前说了不同类型的变量的空间大小是不一样的,我们的int是4字节,char类型是1个字节,请问他们是如何可以相互赋值的?这里就不得谈两个话题,一个是整形提升,一个是截断问题.

数据存储

谈一个耳熟能详的知识点,在计算机中我们存储的数据是补码,这一点我暂时认为大家非常了解,当然如果不太熟悉,可以去看后面的博客.

整型提升

由于CPU以四个字节计算速度快,所以在小于四个字节的的整形数据进行计算时,计算机隐形的将数据提升为四个字节,也就是补足32个比特位,下面是补全的规则.

  • 无符号前面补 0
  • 有符号前面补符号位

下面我分别举一些例子和大家分享.

//输出什么
#include <stdio.h>
int main()
{
    char a = -1;
    signed char b = -1;
    unsigned char c = -1;
    printf("a=%d,b=%d,c=%d",a,b,c);
    return 0;
}

操作符详解_取整_25

操作符详解_取整_26

截断问题

所谓的数据截断就是我们把大空间的赋值给小空间的,编译器会自动的截断,把相应空间的数据给赋值过去,看代码.

#include <stdio.h>
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0;
}

操作符详解_操作符_27

操作符详解_操作符_28

标签:main,int,详解,取整,printf,操作符,include
From: https://blog.51cto.com/byte/6768766

相关文章

  • 大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型
    大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型应用以及各个版本之间的区别详解1.GPT模型1.1GPT模型简介在自然语言处理问题中,可从互联网上下载大量无标注数据,而针对具体问题的有标注数据却非常少,GPT是一种半监督学习方法,它致力于用大量......
  • 大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型
    大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型应用以及各个版本之间的区别详解1.GPT模型1.1GPT模型简介在自然语言处理问题中,可从互联网上下载大量无标注数据,而针对具体问题的有标注数据却非常少,GPT是一种半监督学习方法,它致力于用大......
  • PHP-FPM的配置详解
    1php-fpm的配置详解  和LAMP不同的是,在LNMP架构中,php-fpm作为独立的一个服务存在,既然是独立服务,那么它必然有自己的配置文件。php-fpm的配置文件为/usr/local/php-fpm/etc/php-fpm.conf,它同样也支持include语句,类似于nginx.conf里面的include。  Nginx可以配置多个虚拟......
  • 接上一条操作符
    #include<stdio.h>//'\0'转义字符,等价与数字0.'0'表示字符0,intmain(){ intnum1=10; intnum2=20; if(num1>num2)printf("%d\n",num1); elseprintf("%d\n",num2); return0;}#include<str......
  • terraform安装与命令详解 zz
    terraform安装与命令详解by wanzi2021-02-25约3703字-预计阅读8分钟 devops|阅读 92安装TerraformMac系统安装12brewtaphashicorp/tapbrewinstallhashicorp/tap/terraformLinux系统安装ubuntu安装123curl-fsSLhtt......
  • 大语言模型的预训练[1]:基本概念原理、神经网络的语言模型、Transformer模型原理详解
    大语言模型的预训练[1]:基本概念原理、神经网络的语言模型、Transformer模型原理详解、Bert模型原理介绍1.大语言模型的预训练1.LLM预训练的基本概念预训练属于迁移学习的范畴。现有的神经网络在进行训练时,一般基于反向传播(BackPropagation,BP)算法,先对网络中的参数进行随机初始......
  • 线性基详解
    线性基详解线性基主要用来解决异或问题线性基的性质原序列的任何一个数都可以由线性基中的若干个元素异或得到线性基中的任何元素互相异或,不可异或出0我们考虑到异或的性质:交换律:如果a1^a2^a3=a4那么a2^a1^a3=a4很容易证明a1^a2=a3则a1^a3......
  • Java方法详解
    Java方法详解方法的定义Java方法是语句的集合,它们在一起执行一个功能方法是解决一类问题的步骤的有序结合方法包含于类或对象中方法在程序中被创建,在其他地方被引用publicclassDemo01{//main方法publicstaticvoidmain(String[]args){intsum......
  • Linux磁盘专题-linux文件系统详解
    这可是我几年前的杰作笔记呀.....当初手写计算都会,现在忘光光....物理硬盘Block的概念和作用硬盘底层一次IO就是读、写一次扇区,一个扇区默认是512Byte。读写大量文件如果以扇区为单位会很慢、性能不好,所以出现了逻辑块的概念(logicblock),也就是硬盘Block。逻辑块Block是......
  • 详解prettier使用以及与主流IDE的配合
    很多前端小伙伴在日常使用prettier的时候都或多或少有一点疑惑,prettier在每一个IDE中究竟是怎样工作起来的,为什么配置有时候生效,有时又毫无效果。为了让我们的前端小伙伴更加熟悉这块,本文将对prettier在主流IDE中的使用过程一探究竟。prettier是什么在介绍prettier如何集成到IDE......