详解操作符

目录

1 操作符分类

2 算术操作符

3 移位操作符

原码反码补码

3.1 左移操作符

3.2 右移操作符

4 位操作符

4.1 不创建临时变量,进行两个数交换

5 赋值操作符

6 单目操作符

 7 关系操作符

8 逻辑操作符

9 条件操作符

 10 逗号表达式

 11 下标引用、函数调用和结构成员

11.1 [ ] 下标引用操作符

11.2 ( )函数调用操作符

          11.3 访问一个结构的成员

 12 表达式求值

12.1 隐式类型转换

 12.2 算数转换

12.3 操作符的属性


励志模块

日益努力 而后风生水起 众生皆苦 你也不能认输


1 操作符分类

操作符包括:算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员。

2 算术操作符

加 减 乘 除 取模(取余)

(1)除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。

(2)对于/操作符,如果两个操作数都是整数,执行整数除法。只要有一个浮点数,执行的就是浮点数除法。

(3)%操作符的两个操作数必须为整数,返回的是整除之后的余数。

3 移位操作符

<< 左移操作符 >>右移操作符

移位操作符的操作只能是整数  

左移和右移的都是补码,但是打印出来的都是原码对应的十进制。补码减一,除符号位 其它按位取反

知识点:

原码反码补码

整数有3种二进制的表示形式(任何整数都可以表示出原码 反码 补码) 

整数在内存中储存的是补码

正整数:原码 反码 补码 相同

负整数: 原码 反码 补码不同,要进行计算

 移位操作符,正数不用管 ,负数记得补码变原码。

3.1 左移操作符

移位规则: 左边抛弃、右边补零(指对应二进制的左移和右移)

知识点: a的值还是3,并没有发生变化,只是一种运算。      a<<1只是展现a左移一位的效果,a的值 在没有赋值的情况下 并没有发生变化。

3.2 右移操作符

移位规则:

首先右移运算分两种:(到底是逻辑右移还是算数右移,取决于编译器。我们常见的编译器都是算术右移)

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

2.算术移位:左边用原该值的符号位 填补,右边丢弃。

(注意,负数,回归原码是10000000000000000000000000000001,这个数是-1,不是2^32+1)

警告:

可以左移正数,可以右移正数,但是不可以左移负数 ,右移负数。例如:a<<-3.这是标准为定义的。

4 位操作符

 注意:(正负数都可以,补码进行操作,打印的是原码)

对应的操作也是二进制位的操作(补码操作)(int 32位)

 上图都是正数操作,以便方便叙述,所以前面的0在这里省略。

4.1 不创建临时变量,进行两个数交换

代码1展示:(有局限性,不能解决全部情况,因为int是有范围限制的,万一a和b这两个数非常大,a+b超过int的范围,那么就会出现错误)

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

代码2展示:

两个相同的数异或,是0;0和任何数异或,还是任何数。a^b^b = a

#include <stdio.h>
int main()
{
	int a = 2;
	int b = 3;
	printf("%d %d\n", a, b);
	a = a ^ b;
	b = a ^ b;//a^b^b = a
	a = a ^ b;//a^b^(原来的a) = 原来的b
	printf("%d %d", a, b);
	return 0;
}

5 赋值操作符

=   赋值操作符

a = b = x+1;    分析:x+1的值赋给b,的值赋给a,但是不提倡用(不清晰爽朗 不易与调试)

知识点:

左值:是可以放在等号左边,一般是一块空间

右值:是可以放在等号右边的,一般是一个值(a = 10)或者是一块空间的内容(a = 10 b = a)

复合赋值符:

d8bdf5eca99642c6bcfaded5a5b83fd0.png (1044Ã341)

别的都类似于上图所示

6 单目操作符

a+b,+就是一个双目操作符,因为+两边都有数值,单目操作符就是操作符仅一个操作数

 

逻辑反操作(!):在C语言中,0表示假,!0表示真  。例如:10表示真,则!10输出的就是0。

此时友友们就会想,!0会不会就是任意一个数值,答案是不是的,!0的值是固定的,是1。

(! 真变成假,假变成真)

91c3ab0c9b0d434abb93e0c15b18392d.png (753Ã217)

 正值(+),负值(-),一个数a=10,那么+a就是10,-a就是-10;一个数b=-10,那么+b就是-10,-b就是10。

f6d007d5c2894e1fb8fc041e3379a503.png (1154Ã362)

 知识点:

abs(对整数取绝对值)   fabs(对小数取绝对值)#include <math.h>

取地址符号(&),取地址用的,可以知道储存在内存中的地址。

 操作数的类型长度(以字节为单位)(sizeof):计算的是变量或者类型所创建变量占据内存的大小。

978925c622bb4e8a8abeac8415f0bf6f.png (837Ã293)

 上图中把a去掉,就是类型,sizeof(类型)也可以。int arr[10]呢?答案是也可以的

sizeof(类型)括号不可以省略,变量可以 但是中间需要一个空格

 对一个数的补码的二进制按位取反(~)(0变成1,1变成0),打印的是原码

 0的原码反码补码都是000000000000000000000000000000000

给一个数10 ,  00000000000000000000000000001010,把倒数第三个0变成1,这个数按位或0000000000000000000000000000000100(也可以表示成1<<2)即可变成000000000000000000000000000001110

 现在倒回去,把00000000000000000000000000001110的倒数第三个数字变成0,这个数按位与11111111111111111111111111011(按位取反 1<<2)即可

前置、后置++  前置后置--

前置++:先++,后使用         后置++:先试用,后++

 dde10e07333144a7b0f7e10e3d9499f3.png (1096Ã331)

++a,那么结果都是11

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

#include <stdio.h>
int main()
{
	int a = 10;//在空间内存创建一个变量a,内存的地址叫做m,
	int* pa = &a;//指针变量int*   创建一个变量pa存放地址m
	*pa = 20;//通过指针找到a,用*pa,改变pa的值就是改变a的值
	printf("%d ", a);
	return 0;
}

*pa就是a,&*pa与&a相同。(不管什么元素的指针,它的大小4个字节就是8个字节,例如:不在主函数内的sizeof(ch)的值为4/8.。,char  ch[10] ={0}, (×86环境——32个字节——4个字节)(×64环境——64个字节——8个字节))

 强制类型转换(类型):例如,3.14是一个double类型数字,把它转换成int类型,即 (int)3.14 

05f86e465fbe48f3ae4253c49289d807.png (1014Ã251)

int b =  3.14会报错 ,为了不报错加上(int)

 7 关系操作符

 这个就比较简单了,就是大于,大于等于,小于,小于等于,不等于,等于(注意,判断相等,用两个等号)

8 逻辑操作符

 按位与和按位或 关注二进制,   逻辑与和逻辑或只关注真假

1<a<5 这样写是错误的,如果a = 2,1<2为真,就为1,1<5为真。

 因为a++为后置++,a++为1,a为2,所以逻辑或,一真就全部为真,所以++b,d++并没有计算,所以b和d仍然为2,4.所以a b c d为2 2 3 4. 

总结:

&&左操作符为假,右边不计算                         || 左操作为真,右边不计算

9 条件操作符

这个操作符也叫三目操作符

 条件操作符就是简化if语句。exp1为真,就输出exp2, exp1为假,就输出exp3.

if语句

5a88081aa7c943c8b88d859f69c0d536.png (682Ã375)

 简化if语句

c33b8400e40e46b9b5319a6148ab9dd9.png (772Ã362)

 10 逗号表达式

逗号表达式,就是用逗号隔开的多个表达式。

 逗号表达式会从左向右依次计算,整个逗号表达式的结果是最后一个表达式的结果。

1eeab28eff5b4ffbb64ce969556df5a7.png (795Ã405)

if(a >b,c = b/2,d > 0)   这个语句中起作用的是d > 0. 

 11 下标引用、函数调用和结构成员

11.1 [ ] 下标引用操作符

操作数:一个数组名+一个索引值

int arr[ 10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n",arr[7]); 

上面的[ ]就是下标引用操作符,arr ,7 就是操作数

arr数组首元素地址,arr+7就是第8个元素的地址,*(arr+7)就是arr[ 7 ],*(arr+7)可以写成*(7+arr),那么就可以写成7[ arr ].

11.2 ( )函数调用操作符

test( )中的 函数调用操作符,在函数那个章节有详细说明,在这里就不赘述了

11.3 访问一个结构的成员

.    结构体.成员名

->  结构体指针->成员名

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	double score;
};
int main()
{
	struct Stu s = { "zhangsan", 20, 85.5};
	printf("%s %d %.1lf\n", s.name, s.age, s.score);//结构体变量.结构体成员
	struct Stu* ps = &s;
	printf("%s %d %.1lf\n", (*ps).name, (*ps).age, (*ps).score);
	printf("%s %d %.1lf", ps->name, ps->age, ps->score);//结构体指针->结构体变量
	return 0;
}

 12 表达式求值

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

char short(4字节以下) 进行整形提升(隐式类型转换) int   long    long long    float    double  等(4个以及4个字节以上)这些进行算数转换

12.1 隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。

如何进行整体提升呢?

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

代码展示:

#include <stdio.h>
int main()
{
	char a = 5;//这个5是整数类型,但是存在在char类型
	//0000000000000000000000000000101(整数5)
	//00000101 char只能存在8个  截断
	char b = 126;//00000000000000000000000001111110
	//01111110   b
	//00000101     a
	//当a和b相加的时候,a和b都是char类型 表达式计算就要发生整形提升
	char c = a + b;//a和b进行整形提升后,进行运算。a和b被提升为普通整形,然后进行加法运算
	//因为这两个数的01111110  00000101 最高位都是0,(按照符号位来提升)所以进行整形提升后
	//提升后变成了 00000000000000000000000001111110  0000000000000000000000000000101 
	//然后二进制数字进行相加,0000000000000000000000000010000011
	//相加后要放进char ,所以截断,10000011(c)
	//打印的是%d 又进行整形提升 11111111111111111111111110000011 这个是补码,打印为原码 变成原码即可
	//  11111111111111111111111110000011   补码
	//  11111111111111111111111110000010    反码
	//  10000000000000000000000001111101     原码
	//   -125
	printf("%d\n", c);
	return 0;
}

 12.2 算数转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double
double
float
unsigned long int
long int
unsigned int

int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为较高的另外一个操作数的类型后执行运算。

sizeof内部表达式不参与计算,仅仅判断类型即可

 

12.3 操作符的属性

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级(由高到低)下表中N/A (没有结合性) L-R(从左向右)

 

 以下代码不提倡写,或者就是错误的

 

 

 

 当表达式的路径不确定时,那么这个代码就是错误的

 c + --c;

注释:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得
知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义
的。

非法表达式 : 许多操作符进行干扰,导致在不同编译器下的值不同。

 总结:

我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题。

操作符就到此结束了。

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小刘同学啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值