在学习C语言的过程中,我们都会使用很多的操作符,那么如何加深对这些操作符的理
解呢?如何正确的使用操作符呢?下面我就来带着大家去熟悉操作符。
一、操作符的分类
操作符主要分为以下10中我们经常使用的类别
目录
一、操作符的分类
二、1.算术操作符
2.移位操作符
1. 逻辑移位
2. 算术移位
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.一些特别的操作符使用
整型提升的意义:
下面将计算时的整形提升排序展现给大家
操作符优先级
下面我们来开始我们的第二部分对以上操作符进行讲解
二、1.算术操作符
算术操作符:
+ - * / %
以上的操作符除了 % 在使用的时候需要两边都为整数之外,其他操作符可以及处理整
形也可以处理浮点型,% 之后保留的是俩个数相除之后保留的余数。
当然在使用 / 的时候需要注意,如果 / 的两边的数字都为整数那么算出的结果就保留
整数而不会保留小数部分,当然如果 / 的使用中存在一个浮点型数据那么结果就可以
产生小数。
+ - * 就是我们正常的数学运算这里我们就不过多介绍
2.移位操作符
当然这里的很多小伙伴或许没有听过什么是位操作符,不知道的小伙伴可以去看一下计
算机的存储方式计算机数据存储
这里主要有俩种位操作符
<< 左移操作符 >> 右移操作符
当然需要注意的是对于操作符来说,只能对整数的数据使用为操作符(这个原因是数据在
计算机中的存储方式不同所造成的)
当我们知道数据在计算机中的存储方式为二进制位的时候(我们以32为计算机为操作对
象)例如:
//位操作符的使用
#include<stdio.h>
int main()
{
int a = 1; // 在计算机中的存储为 00000000000000000000000000000001
printf("%d\n",a);
a = a << 1; //当我们将他的二进制位向左移动以为 << 1相当于 a 的二进制位向左移动1位
//他的二进制位变为 0 | 00000000000000000000000000000010 左边多的0丢弃右边补0
printf("%d\n", a);
return 0;
}
可以看到他的运行结果和我们所写的二进制是想对应的 << 的使用为左边为变量 右边为
要进行操作的位数 如 a << 2; 就表示 a 的二进制位向左移动俩位最左边的俩位丢弃,
右边补0
当然如果不进行赋值操作对原来的值不会有所更改
下面是对 >> 操作符的介绍 >> 操作符的使用和 << 操作符差不多但是他们的工作原理
不同 例如:
#include<stdio.h>
int main()
{
int b = 2;//二进制位 00000000000000000000000000000010
printf("b = %d\n", b);
b = b >> 1;//像右移动一位 00000000000000000000000000000001 | 0 超出部分的0被舍弃
printf("b = %d\n", b);
//计算机存储的为补码
int c = -2; //-2的补码为 11111111111111111111111111111110
c = c >> 1; //向右移动一位 11111111111111111111111111111111 | 0 超出部分的0舍弃
printf("c = %d", c);
return 0;
}
例如上述代码,当然细心的小伙伴以及发现了不同当想 右移动的数字为整数时移动的规
则和左移的规则一样,变成负数之后就不同了呢?
当然这个是为了保持数据的一致性,数字的首位为符号位,当我们进行右移的时候如果
直接补0可能会影响数据的正负问题,那么计算机采取的处理方式是,向右移动时,他
本来的符号位上的数字为什么,我们就补什么,如果为0,向右移动就补0,如果为1,
就补上一个1
当然这种移动方式有他们的名称
1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃
计算机右移操作符采取的是算术移位
3.位操作符
对于位操作符来讲主要有以下几个:
& 按位与 | 按位或 ^ 按位异或
当然这些操作符的使用也必须是整数
例如下面的代码展示:
//位操作符
#include<stdio.h>
int main()
{
int a = 1;//二进制为00000000000000000000000000000001
int b = 2;//二进制为00000000000000000000000000000010
// a & b
//00000000000000000000000000000001
//00000000000000000000000000000010
//c的二进制为 00000000000000000000000000000000
int c = a & b;
printf("c = %d\n", c);
return 0;
}
当然这里可能大家就已经看出来了,只要当俩位上的数字相同时才保留,否则就为0,
及为相同为真进行保留,不同为假赋值为0.
那么操作符 | 的使用大家应该也能猜出来了
#include<stdio.h>
int main()
{
int a = 1;//二进制为00000000000000000000000000000001
int b = 3;//二进制为00000000000000000000000000000010
// a & b
//00000000000000000000000000000001
//00000000000000000000000000000011
//c的二进制为 00000000000000000000000000000011
int c = a | b;
printf("c = %d\n", c);
return 0;
}
这里只要有一个数字为真结果就为真,俩个都为假才为假
^ 的使用和上面有所不同为相同为假不同为真
例如:
#include<stdio.h>
int main()
{
int a = 1;//二进制为00000000000000000000000000000001
int b = 3;//二进制为00000000000000000000000000000010
// a & b
//00000000000000000000000000000001
//00000000000000000000000000000011
//c的二进制为 00000000000000000000000000000010
int c = a ^ b;
printf("c = %d\n", c);
return 0;
}
可以通过上述代码的结果看到,如果相同位上的数据相同则为假,不管这个为上的数字
是0或者是1,如果相同位上的数据不同及一个为1一个为0那么就为 真(及为1)。当然
可能小伙伴就要问了,这个代码有什么作用呢,
下面我就来给大家展示一下他的一些功能
例如,交换俩个变量的值,不创建临时变量且不能出现数据溢出的情况
那么我们需要怎么做呢
当然可能会有人想到使用下面代码
#include<stdio.h>
int main()
{
int a = 20;
int b = 30;
a = a + b;
b = a - b;
a = a - b;
printf("a = %d, b = %d", a,b);
return 0;
}
可以看出最后 a 和 b 的值确实进行了交换,但是这种方法漏掉了一直情况,就是当a
和 b 的值都接近他们的类型的最大值,但a 和 b 没有超过这个值,但是a + b就可能会越
界使我们计算的结果出错,如果我们换一直思路,用 ^ 去处理
#include<stdio.h>
int main()
{
int a = 20;
int b = 30;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d, b = %d", a, b);
return 0;
}
我们可以看到,结果和我们想要的一样,那么这个是怎么计算的呢,这个我们可以这样
去理解这种解法 及 a ^ a ==0 a = a ^ b ; b = a ^ b --> b = a ^ b ^ b = a; a = a ^ b -
-> a = a ^ b ^ a = b; 所以结果就进行了交换;而且这个交换都是在32位的二进制内进
行的不存在越界的问题,小伙伴们是不是感觉这种解法很巧妙。
当然 & 的作用也是很大的,例如一个数用 移位操作符去移动一个数,然后用 1 与每次
移动的数进行 & 可以得到这个数中1 的个数,当然我们只对操作符的使用进行介绍,如
果想探究更简单的方式,自己可以在平台进行搜索。
4.赋值操作符
主要为简单赋值和复合赋值
简单赋值:
int weight = 20; 就是一个赋值语句,将20 赋值给变量 weight
也可以进行连续赋值 例如:
int x = y = 10;
这个意思是将 10 赋值给 y 在将 y 的值赋值给 x
复合赋值符:
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
当然他们的使用在这里只是简单的介绍一下,例如:
int a = 20;
a += 10; // 等价与 a = a + 10;
a >> = 1; // 等价与 a = a >> 1;
其他的使用与这个是相同的道理,在这就不做过多介绍
5.单目操作符
下面是一些单目操作符的使用
主要包括:
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
逻辑反操作例如
int a = 10; // 如果 a 为非 0 的数进行逻辑反之后都会变成 0
!a == 0;
int a = 0; // 如果 a 为 0 进行逻辑反之后 会变成 1(及为真);
!a == 1;
正负号大家应该比较熟悉,int a = -10; 当然 int a = +10;也行但是我们一般不写这个 + 号
当然这里的 & 的我们之前讲的为操作符并不相同, 这里的 & 是放在变量名的前面,表
示取出这个变量的地址,通常与指针相结合使用,例如,int a = 10; int *p = &a;
或者当我们求一个操作符的大小时用sizeof例如
printf("%d\n", sizeof(int) );// 可以求出一个int占几个字节
也可以直接使用:
int a = 0;
printf("%d\n",sizeof(a)); // 求的结果为 a的变量类型所对应的字节数
~ 对他的二进制位取反
如 int a = 1;// 二进制为:00000000000000000000000000000001
~a; // 二进制为: 11111111111111111111111111111110
则a = -2;
当然这里的 a++; // 为 a = a + 1; a-- 为 a = a - 1;
当然前置++ / --,与后置 ++ / -- 有所不同
例如:
#include<stdio.h>
int main()
{
int a = 10;
int b = a++;//后置++
printf("a = %d b = %d\n",a,b);
int c = 10;
int d = ++c;//前置++
printf("c = %d d = %d\n",c,d);
}
打印的结果有所不同
a = c = 11; b = 10; d = 11;
6.关系操作符
>
>=
<
<=
!= 不等于的比较
== 等于的比较
需要注意的是在使用 == 的时候不要将 = 弄混,因为 = 为赋值操作,而 == 才是判断操作
7.逻辑操作符
&& 逻辑与
|| 逻辑或
及判断俩个条件或者多个条件时所用的
用 && 时 只有保持 逻辑与两侧的条件都为真结果才为真,否则为假,这里需要注意的
是,如果在进行判断的时候,如果存在运算,当一方为假,后面的逻辑与这不在进行判
断。
在用 || 的时候,只要保持逻辑或的俩侧存在一个为真则结果为真,都为假才是假,当然
这里也有需要注意的点,及在使用逻辑或进行判断的时候,只要有真值为真的情况发
生,就不在进行后面的逻辑判断。如果后面存在运算则不在进行例如:
int a = 10; int b = 0 ; if( (a == 10) || ( b = 2) ); 不熟悉的话肯定会认为,a最后为10
, b 为 2 ,其实并不是,当 a == 10 判断为真的时候后面的判断就不在进行,就是 2
并未赋值给 b 在&&的逻辑判断也相同
8.条件操作符
exp1 ? exp2 : exp3
在这个表达式中,当条件1为真的时候执行条件2,如果条件1为假则执行条件3
例如 a > 3 ? 1 : -1; 这段代码的意思是,如果 a > 3为真,这返回 1 否则返回 -1
9.逗号表达式
逗号表达式的特点就是从前向后计算,将最后的值返回
例如:
int a = 10;
int b = 20;
int d = 0;
int c = (a = a + 10, b = a + b, d = 50);
最后 a = 20; b = 40; d = 50; 并返回d 的结果给 c , c = 50;
10.一些特别的操作符使用
该内容主要为,数组的创建,函数的调用,和结构体的访问对象
例如:
int arr[10]; // 我们创建了一个arr数组,里面有10元素,都是int类型的,需要用的 [ ]
或者我们写了一个加法函数
Add (x,y);// 将x ,y 的值传入进行计算,需要用到的 ( )
或者是在访问结构体成员所使用的 . 或者 ->
当然在计算的时候我们有会经常遇到一些问题,就是关于不同类型的数值直接的计算
最后向大家介绍一下,关于整形提升的一部分知识
当然在进行介绍之前我们先来了解整形提升的意义
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
例如:
char 类型在计算机的存储过程中只占一个字节;
int 占4个字节
那么他们如何进行计算呢?
例如我们将一个超过 255的数字存放在char类型中,会在存储的时候进行截断
#include<stdio.h>
int main()
{
int a = 256;
char c = a;
printf("%d", c);
return 0;
}
可以看出我们最后打印出的结果是 0;
那么为什么呢?
a = 256; 他的二进制存储为 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000
char b 的内存只有 0000 0000那么大,当将 int 类型的 a 的数据放在b里面时,存储的
只是后面的一个字节大小的数据 0000 0000
当然,如果我们将一个char类型的数据赋值给一个int类型的数据时,就会发生整形提升如:
#include<stdio.h>
int main()
{
char a = 128;
int b = a;
printf("b = %d", b);
return 0;
}
这里我们可以看到,b中存放的不是128,而是-128,那么为什么呢?
我们知道计算机在存放char类型的时候只开辟了一个字节的空间大小,当要将这个赋值
给int类型的变量的时候需要整形提升,
如上述代码中 ,
char a = 128; 他的二进制位 1000 0000
而将这个数赋值给int 类型的b 的时候 对 1000 0000进行整形提升,因为首位代表符号
位所以,提升的时候看符号位,符号位为1,整形提升全补1
b 的二进制为 1111 1111 1111 1111 1111 1111 1000 0000,为补码
而首元素为1符号位为负数这反码为 1111 1111 1111 1111 1111 1111 0111 1111
原码为 1000 0000 0000 0000 0000 0000 1000 0000 为 - 128
最后打印结果为 b = -128
下面将计算时的整形提升排序展现给大家
long double
double
float
unsigned long int
long int
unsigned int
int
char
从下往上进行提升,如果运算中有这些的话
当然还有一些复杂表达式的运算顺序,也想大家展示一下