第十章(操作符)

1. 操作符的分类

2. ⼆进制和进制转换

3. 原码、反码、补码

4. 移位操作符

5. 位操作符:&、|、^、~

6. 单⽬操作符

7. 逗号表达式

8. 下标访问[]、函数调⽤()

9. 结构成员访问操作符

10. 操作符的属性:优先级、结合性

11. 问题表达式求值

                      书中自有颜如玉,书中自有黄金屋

一、操作符分类
在这里插入图片描述
其中有些操作符我们已经学过了,但是我们今天在介绍一部分,今天的这些部分都是与二进制有关的所以我们今天来先结束二进制。

二、二进制和进制转换
我们在学数学中偶尔会听到“2进制,8进制,10进制,16进制”,那这些是什么意思呢?
其实这些进制的表示都是对一个数的不同表达形式罢了。

我们来举20数值的的不同进制的表达形式

在这里插入图片描述
我们来类比10进制来学2进制
在10进制中:
1.10进制满10进1
2. 只能出现0~9的数字
则在2进制中
1.2进制满2进1
2.只能出现0~2的数字

2.1进制的转换

10进制的123表示的值是253,为什么是这个值呢?这是因为10进制每一位都是有权重的
在这里插入图片描述
在这里插入图片描述
我们知道2进制和10进制是非常类似的,那么如果是2进制的1101应该怎么理解呢?
在这里插入图片描述
2.1.1
10进制转2进制数字
方法:除2取余法
在这里插入图片描述

2.2 2进制转8进制和16进制
8进制的数字的每一位都是0~7,0到7的数字各自写成2进制,最多3个二进制数就够了。
我们拿7来举个例子如下:
在这里插入图片描述
所以我们可以知道从二进制序列中每3个二进制位转化位1个8进制位。
在这里插入图片描述
2.2.2 二进制位转换位16进制(与2进制位转换成16进制位的方法是一样的)
在这里插入图片描述
三、原码、反码、补码
整数的2进制有三种表示的方法:原码、反码、补码

对于有符号的整数(负数)的三种表示方法都有符号位数值位两部分。

我们用 0 表示正 ,用1表示负。

正整数的原码,反码,补码都是一样的
负整数的原码,反码,补码都是不一样的

原码:将数字直接按照正负数的规则翻译成二进制得到的就是原码
反码:原码的符号位不变,其他数值位按位取反就可以的到反码
补码:反码加1就是补码

补码直接取反+1也可以得到原码

对于整形来说:内存中储存的是补码

这是为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

举个例子帮大家了解:
在这里插入图片描述

四、位移操作符
1.<<:左移操作符
2.>>右移操作符
注意:位移操作符操作的只能是整数

4.1左移操作符
规则:左边抛弃、右边补0

#include<stdio.h>
int main()
{
	// 正整数的原码反码补码相同
	//10的原码为   00000000000000000000000000001010
	//左移操作符的规则:左边边抛弃右边补0 ;
	//00000000000000000000000000001010 变为   00000000000000000000000000010100
	//我们根据操作猜测为4 + 16 = 20
	int a = 10;
	int b = a << 1;
	printf("%d", b);
	return 0;
}

在这里插入图片描述
打印出来的结果就是20,说明我们对了。

4.2 右移操作符
规则:右移操作符运算分为两种
1.逻辑右移:左边用0填充,右边丢弃
2.算术右移:左边用原来的符号填充,右边丢弃.
(一般用这个)

int main()
{
	int num = -10;
	//-10的原码反码补码不一样的
	//原码10000000000000000000000000001010
	//反码11111111111111111111111111110101
	//补码11111111111111111111111111110110
	
	//    11111111111111111111111111111101  //算术右移  (正常)
	//   原码 10000000000000000000000000000011   =-3
	//    00111111111111111111111111111101  //逻辑右移
	//   原码 11000000000000000000000000000011    //这里我们发现逻辑右移出了点问题
	int num2 = num >> 2;
	printf("%d", num2);
	return 0;
}

在这里插入图片描述
这里我们需要注意的是:对于位移操作符不能移动负数位(这个标准是没有定义的)。

int main()
{
	int num = -10;

	int num2 = num >> -2;  //error 
	printf("%d", num2);
	return 0;
}

五、位操作符:& 、|、 ^、 ~
在这里插入图片描述
注意:这里的操作数必须为整数

直接用代码来实践

#include<stdio.h>
int main()
{
	int num1 = 5; //5的原码(就是补码) 00000000000000000000000000000101
	int num2 = -6;
	//-6 的原码 1000000000000000000000000000110
	//反码      1111111111111111111111111111001
	//补码      1111111111111111111111111111010


	printf("%d ", num1 & num2);
	 //5的原码     0000000000000000000000000000101
	//6的补码      1111111111111111111111111111010
	//&后得        0000000000000000000000000000000
	//原码为       0000000000000000000000000000000
	//0
	printf("%d ", num1 | num2);
	//5的原码      0000000000000000000000000000101
	//6的补码      1111111111111111111111111111010
	//|后得        1111111111111111111111111111111(符号位不变)
	//原码为       1000000000000000000000000000001
	//-1
	printf("%d ", num1 ^ num2);
	//5的原码      0000000000000000000000000000101
	//6的补码      1111111111111111111111111111010
	//^后的        1111111111111111111111111111111
	//原码         1000000000000000000000000000001
	//-1
	printf("%d ", ~5);//对一个数得
	//5的原码     0000000000000000000000000000101
	 //~后得      1111111111111111111111111111010
	//原码        1000000000000000000000000000110
	  //-6
	return 0;
}

我们来出一道题目:
实现两个变量得值交换,但是不能创建第三个变量


int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;     //  a^b^b =a ,a^b^a = b
	b = a ^ b;   //这里我们可以通过^操作符来进行不创建变量来来交换数字
	a = b ^ a;
	printf("a=%d b=%d", a, b);
	return 0;
}

六、单目操作符
单目操作符的表面意思就是对一个数字进行操作

在这里插入图片描述
这里我们只剩 * 和 & 没有见过等我们学习指针的时候来解释

七、逗号表达式
在这里插入图片描述
逗号表达式,就是用逗号隔开的多个表达式
逗号表达式,从左向右依次执行整个表达式的结果是最后一个表达式的结果

int main()
{
	int a = 10;
	int b = 20;
	int c = (a = b + a, b < a, b = a + b);
	//这里的c的值是多少呢?
	//从左向右执行得到的是c=50
	printf("%d", c);
	return 0;
}

在这里插入图片描述
八、下标访问[]、函数调用()

8.1 []下标引用操作符
操作数:数组名+[常量数字]

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7 };
	//[]下标引用操作符
	//arr 数组名
	//arr+[常数],就可以拿到素组元素对应的下标
	printf("%d", arr[2]);
	return 0;
}

在这里插入图片描述

8.2 函数调用操作符
函数调用操作符()
函数名加上()就可以调用我们所写的函数

 void text1(void)
{
	 printf("咯咯哒\n");
}
 void text2(char* pr)
 {
	 printf("%s\n", pr);
 }

int main()
{
	//()是函数调用操作符
	//text1是函数名
	text1();
	//text加上()就可以调用函数
	text2("ikun");
	return 0;
}

在这里插入图片描述

九、结构成员操作符
9.1 结构体

C 语言给我们提供了一系列的类型 int ,char ,double …也给我们提供了对应的数组。但是这些类型往往都是单一的,如果我们想描述一本书,一个学生,只有这些单一的类型是不够的。

对于以一个学生:名字,年龄,学号…

C语言为了解决这种问题给我们提供了一种自定义的数据类型,让我们可以自由的设计自己需要的情况。
在这里插入图片描述
9.1.结构体的声明
在这里插入图片描述
9.1.2 结构体变量的定义和初始化


struct nt
{
	int x;
	int y;
}p1;//全局变量(声明的时候同时定义)

struct p
{
	char name[20];
	int age;
};
int main()
{
	struct nt p2 = { 1,2 }; //局部变量
	printf("%d %d\n", p2.x, p2.y);

	struct p p3 = { "lik",{10} };//顺序变量初始化

	printf("%s %d\n", p3.name,p3.age);

	struct p p4 = { .age = 20 , .name = "wkl" };//指定变量初始化
	printf("%s %d\n", p4.name, p4.age);


	return 0;
}

9.2 结构体成员访问操作符
在这里插入图片描述

struct s
{
	int a;
	int b;
};
int main()
{
	struct s s1 = { {10},{20} };
	printf("%d %d", s1.a, s1.b);   // .的左右两边有两个数值
}

使用方式:结构体变量 . 成员名

9.2.2 结构体变量的间接访问
结构体也是有指针的,那么如果我们拿到的是结构体的指针是如何使用的呢?

struct s
{
	int a;
	int b;
};
int main() 
{
	//int a = 10 ;
	//int * pr = &a;
	struct s s1 = { {10},{20} };
	struct s* pr = &s1;
	//这里我们需要在引入一个操作符,(->)
	printf("%d %d", pr->a, pr->b);   // ->的左右两边有两个数值
}

使用方式:结构体指针 -> 成员名

这里我们主要了解的是操作符,在以后的学习中我们更详细的了解一下。

综合举例

struct s
{
	char name[15];
	int age;
};

void print_(struct s S1) //直接访问
{
	printf("%d %s\n", S1.age, S1.name);
}
void prrr_(struct s* prr)//间接访问
{
	printf("%d %s", prr->age, prr->name);
}

int main()
{
	struct s s1 = { "lisi",20 };
	struct s* pr = &s1;
	print_(s1);
	prrr_(pr);
}

十、操作符的属性:优先级、结合性
在数学中我们通过数字的一些规则可以先进行数字的先后操作顺序,那么在C语言中我们也有自己的一套方法。

即:操作符的优先级和结合性,通过这两个方法我们就可以确定表达式的计算顺序。

10.1 优先级


int main()
{
	int a = 5 + 5 * 5;
	return 0;
}

对于该数值a答案是多少呢? 是先计算5 + 5 然后 再5,还是先计算 55 再加5呢?
操作符的优先级不同决定了数值的不同,我们肯定知道的是 / * 的优先级是大于 + -的。所以这里是先计算的是 5 +5

10.2 结合性
当两个操作符的优先级相同的时候优先级就没办法确定先算那个了?

int main()
{
	int num = 5 * 5  / 3;
	return 0;
}

在这里插入图片描述
这里的计算值为 8 我们发现就是先计算的是5 * 5 然后再/3。
这里就可以知道,当两个操作符的优先级相同时,运算顺序是从左到右的(大部分)只有赋值运算符=是从右到左的

我们需要记住的优先级顺序如下:
在这里插入图片描述
)的运算符最高我们可以运用()来改变其它运算符的优先级

其它运算符优先级我们可以参考其表
https://zh.cppreference.com/w/c/language/operator_precedence

十一、问题表达式解析

11.1 表达式1
在这里插入图片描述
这里的的计算顺序不同,也就可能会导致一些问题(这个问题恰好是满足)
11.2 表达式 2

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

在这里插入图片描述

在这里插入图片描述

这里我们发现,在不同的编译器上的结果是不同的,说明编译器遇见这种复杂的代码也出现了问题。

总结;在我们学习了操作符的优先级和结合性之后,我们也不能写那种太过复杂的代码
写代码的时候还是需要一步一步的写,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值