七千字操作符超详解
目录
操作符简介:
🍓一、操作符分类
🍓二、算数操作符 (+ - * / %)
🍓三、移位操作符(<>)
🍍1.整型的二进制:
🍍2.左移操作符(<<)
🍍3.右移操作符
🍓四、位操作符 (& | ^)
🍍练习一(变态的面试题):
🍇思路1:
🍇思路2:
🍍练习二:
🍓五、赋值操作符 (=)
🍍复合操作符
🍓六、单目操作符
🍍!操作符
🍍- +操作符
🍍& 操作符
🍍sizeof操作符
🍍~ 操作符
🍍-- ++的前置与后置操作符
🍍* 操作符
🍍(类型)操作符
🍓七、关系操作符 (> <)
🍓八、逻辑操作符 (&& ||)
🍓九、条件操作符(?)
🍓十、逗号表达式( ,)
🍓十一、下标引用、函数调用和结构成员([ ]())
🍍1.[ ] 下标引用操作符 操作数:一个数组名 + 一个索引值
🍍2. ( ) 函数调用操作符
🍍3. 访问一个结构的成员
操作符简介:
操作符是嵌在每一条指令中的,换句话说,指令系统的每一条指令都有一个操作符。
🍓一、操作符分类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
🍓二、算数操作符 (+ - * / %)
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
🍓三、移位操作符(<>)
🍍1.整型的二进制:
整数在计算机中的存储规则:整数是以二进制的形式来存储的。
- 正数的原码、反码、补码相同
例如:
7(int类型)
00000000000000000000000000000111 - 原码
00000000000000000000000000000111 - 反码
00000000000000000000000000000111 - 补码
- 反码的原码、反码、补码需要计算
例如:
-7(int类型)
10000000000000000000000000000111 - 原码
11111111111111111111111111111000 - 反码(原码的符号位不变,其他位按位取反就是反码)
11111111111111111111111111111001 - 补码(反码+1就是补码)
整型在内存中存放的是补码。
🍍2.左移操作符(<<)
移位规则:左边抛弃、右边补零(如下图)。
使用方法:
num<<n(左移n位,n为正整数)
下面是测试代码机器结果:
这里我们可以注意到一个小细节:num<<1并不会改变num本身的值。
🍍3.右移操作符
右移的运算分为两种:
1. 逻辑移位 左边用0填充,右边丢弃
2. 算术移位 左边用原该值的符号位填充,右边丢弃
两种位移的离职如下:
注意哦:我们不可以移动负数位,因为这是标准未定义的。
🍓四、位操作符 (& | ^)
& 按(二进制)位与——1 1为1,1 0为0,0 0为0
| 按(二进制)位或——1 1 为1,1 0为1,0 0为0
^ 按(二进制)位异或——相同为0,相异为1
注:他们的操作数必须是整数。
下面是使用实例:
&
|
^
🍍练习一(变态的面试题):
不能创建临时变量(第三个数)实现两个数的交换
🍇思路1:
答案:
int main(){int a = 3;int b = 5;printf("交换前:a=%d b=%d\n", a, b);a = a + b;b = a - b;a = a - b;printf("交换后:a=%d b=%d\n", a, b);return 0;}
这个方法巧妙地利用的数学的运算,但是因为我们的整型是4个字节有自己的取值范围,所以与下面的方法相比,有溢出的风险。
🍇思路2:
由3^3 = 0 得到 a^a=0
//011—3
//011—3
//000—3^3
由0^5= 5 得到 0^a = a
//000—0
//101—5
//101—5
//3^3^5 =0^5= 5
//3^5^3 = 5
异或操作符支持交换律
解题关键就是交换律
答案(直接想非常难,我就直接放出来了):
int main(){int a = 3;int b = 5;printf("交换前:a=%d b=%d\n", a, b);a = a ^ b;//a=3^5b = a ^ b;//3^5^5 --> b=3a = a ^ b;//3^5^3 --> a=5printf("交换后:a=%d b=%d\n", a, b); return 0;}
这里面有三个a^b:
1:a=a^b:在这里得到a=3^5
2:b=a^b:在这里得到b=3^5^5,通过交换律得到,b=5^5^3,有a^a=0、0^a=a,得:b=3
3:a=a^b:在这里得到a=3^5^3,通过交换律得到,a=3^3^5,与上面同理得:a=3
🍍练习二:
题目:
编写代码实现:求一个整数存储在内存中的二进制中1的个数。
思路:
整数储存在内存中是以二进制的形式,这里它就问了1的个数,我们不考虑原码补码之类的。
在上面的位运算我们知道,1^1=0,那么我们就可以拿这个二进制的每一位和一个1按位与,如果结果是一个非零的数字就说明内存中这个二进制有一个1了。比如下面:
00000000000000000000000000000011————数字3
00000000000000000000000000000001————数字1
00000000000000000000000000000001————3&1的结果
我们要检测数字三左边的位上还有没有1,就可以用<<将数字1左移一位,在次和它按位与一次。。
00000000000000000000000000000011————数字3
00000000000000000000000000000010————数字1<<1
00000000000000000000000000000010————3&(1<<1)的结果
我们在这里又得到了一个非零数,说明内存中二进制的的二位也是一个1。以此类推,我们循环32次就可以。
答案:
int main(){int a = 3;int b = 1;int c = 0,n=0;for (int i = 0; i < 32; i++){c = a & b;if (c != 0)n++;b <<= 1;}printf("%d\n", n);return 0;}
🍓五、赋值操作符 (=)
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋 值。
int weight = 120;
//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续赋值,比如:a=x=y+1;
但这样可读性不强,我们更建议写成
x=y+1;
a=x;
这种形式。
🍍复合操作符
- +=
- -=
- *=
- /=
- %=
- >>=
- <<=
- &=
- |=
- ^=
这些操作符的使用方法和+=一致。
🍓六、单目操作符
单目的意思是只有一个需要处理的操作数,举个例子:(表达式1)?(表达式2):(表达式3);这样的结构就称之为三目操作符。
- ! 逻辑反操作
- - 负值
- + 正值
- & 取地址
- sizeof 操作数的类型长度(以字节为单位)
- ~ 对一个数的二进制按位取反
- -- 前置、后置--
- ++ 前置、后置++
- * 间接访问操作符(解引用操作符)
- (类型) 强制类型转换
🍍!操作符
通常用在if()语句中,构成if(!)的结构。
🍍- +操作符
和数学的规律一张,-可以改变符号,但是+不能。
🍍& 操作符
作用:将一个变量的地址取出。
使用方法:&a
🍍sizeof操作符
很多小伙伴觉得sizeof()是一个库函数,其实不是,这是一个操作符。可以测所在空间的大小,并且是以字节为单位。
一个思考题:
#include void test1(int arr[]){ printf("%d\n", sizeof(arr));//(2)}void test2(char ch[]){ printf("%d\n", sizeof(ch));//(4)}int main(){ int arr[10] = {0}; char ch[10] = {0}; printf("%d\n", sizeof(arr));//(1) printf("%d\n", sizeof(ch));//(3) test1(arr); test2(ch); return 0;}问:(1)、(2)两个地方分别输出多少?(3)、(4)两个地方分别输出多少?
答案:
(1)40 (2)4或者8 (3)10 (4)4或者8
原因:数组arr有十个元素,一个元素占4个字节,所以(1)是40。数组ch有是个元素,一个元素占1个字节,所以(3)是10
在test1()和test2()函数中接收到的其实是数组的地址,一个地址只占4或8个字节大小 。
🍍~ 操作符
作用是将一个二进制数取反
~00000000000000000000000000000000
得到11111111111111111111111111111111
🍍-- ++的前置与后置操作符
前置++/--,先++/--,后使用
比如:
int a=3;
int b=++a;
b=4,a=4.
后置++/--,先使用,再++/--
比如:
int a=3;
int b=a--;
b=3,a=2.
🍍* 操作符
间接访问操作符(解引用操作符)其用法就是在地址前使用这个操作符就可以直接使用这个地址里面储存的数据。
比如:
int a=3,int *P;
P=&a;
* P=2
结果a的值也变成2了
🍍(类型)操作符
强制类型转换,可以将一个类型的数据转换成另外一个数据类型
比如:
double b=10;
int a=(int) b;
这样可以将b的double类型变成int类型再赋值给a
🍓七、关系操作符 (> <)
- >
- >=
- <
- <=
- != 用于测试“不相等”
- == 用于测试“相等”
我们再进行字符串的比较的时候是不能直接用“==”
//例如:if("abc"=="abcdef")
这样写只会比较它们的地址,这个比较会返回1。字符串的比较需要用strcmp()这个库函数,被包含在这个头文件中。
这里指的注意的点就是:“==”经常容易写成"=".
🍓八、逻辑操作符 (&& ||)
- &&
- ||
这里要注意点是&&的优先级要高于||的
🍓九、条件操作符(?)
exp1 ? exp2 : exp3
使用方法:
我们可以把:
if (a > 5)b = 3;elseb = -3;
改成:
(a>5)?3:-3;
🍓十、逗号表达式( ,)
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。
整个表达式的结果是最后一个表达式的结果。
int a = 1;int b = 2;int c = (a > b, a = b + 10, a, b = a + 1);printf("c=%d\n", c);
看一下这段代码,最后打印c出来。
答案:13.
🍓十一、下标引用、函数调用和结构成员([ ]())
🍍1.[ ] 下标引用操作符 操作数:一个数组名 + 一个索引值
这里其实有一个骚操作 :
arr [n],这个的原理其实是:首地址arr+n。根据加法交换律得:arr+n=n+arr。
那么我们的arr[n]可以写成[n]arr。
不过一般不建议这样写,因为对别人不友好可读性太差。
🍍2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
🍍3. 访问一个结构的成员
- . 结构体.成员名
- -> 结构体指针->成员名