目录
一、前言
操作符呢又叫运算符,C语言中有着各种各样的操作符,这篇文章讲的是各种操作符的介绍。
二、算术操作符
算数操作符一般作用于两个操作数之间,我们学数学应该 很熟悉
1.加号 “ + ”
+ 实现两个数的相加
#include<stdio.h>
int main()
{
int num1 = 1;
int num2 = 2;
int Add = num1 + num2;
printf("Add == %d",Add);
return 0;
}
2.减号 “ - ”
- 实现两个数的相减
#include<stdio.h>
int main()
{
int num1 = 4;
int num2 = 2;
int Red = num1 - num2;
printf("Red == %d",Red);
return 0;
}
3.乘号 “ * ”
* 实现两个数的相乘
#include<stdui.h>
int main()
{
int num1 = 4;
int num2 = 2;
int Take = num1 * num2;
printf("Take == %d", Take);
return 0;
}
4.除号“ / ”
/ 实现两个数的除法
#include<stdio.h>
int main()
{
int num1 = 5;
int num2 = 2;
float num3 = 5;
float num4 = 2;
int Rem1 = num1 / num2; //整形除整形
float Rem2 = num3 / num4; //浮点型除浮点型
float Rem3 = num3 / num2; //浮点型除整形
float Rem4 = num1 / num4; //整形除浮点型
printf("Rem1 == %d\n", Rem1);
printf("Rem2 == %f\n", Rem2);
printf("Rem3 == %f\n", Rem3);
printf("Rem4 == %f\n", Rem4);
return 0;
}
为什么Rem1跟 Rem2 Reme3 Rem4值会不一样呢
那是因为我们规定
当两个操作数为整数相除,执行的是整数除法
当两个操作数中有一个或两个都是浮点型的话,执行的是浮点型除法
5.除法取余数" % "
% 实现两个数的除法取余数操作
取模操作符 的两个操作数必须是两个整形 !!!
#include<stdio.h>
int main()
{
int num1 = 5;
int num2 = 2;
int Sur1 = num1 % num2;
printf("Sur == %d\n", Sur1);
return 0;
}
三、移位操作符
移位操作符一般作用于两个操作数之间,移位操作符移动的二进制的位
大家都知道 , 整数在内存中存储的是补码 , 所以移位针对的是补码
正数:原码 反码 补码相同
负数:原码 反码 补码需要计算(符号位不变其他位 取反加一)
1.左移操作符<<
移位规则:左边抛弃、右边补0
-
正数
#include<stdio.h>
int main()
{
int num1 = 3;
int num2 = num1 << 1;
printf("num1 == %d num2 ==%d",num1,num2);
return 0;
}
上面就是正数一个例子
3
原码为 0000 0000 0000 0000 0000 0000 0000 0011
反码为0000 0000 0000 0000 0000 0000 0000 0011
补码为0000 0000 0000 0000 0000 0000 0000 0011
3 <<1
补码向左整体移一位(左边舍弃右边取0)
补码为0000 0000 0000 0000 0000 0000 0000 0110
反码为0000 0000 0000 0000 0000 0000 0000 0110
原码为 0000 0000 0000 0000 0000 0000 0000 0110
还原回原码是 6
-
负数
#include<stdio.h>
int main()
{
int num1 = 3;
int num2 = num1 << 1;
printf("num1 == %d num2 ==%d",num1,num2);
return 0;
}
上面就是负数一个例子
-3
原码为 1000 0000 0000 0000 0000 0000 0000 0011
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1100
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1101
-3 << 1
补码向左整体移一位(左边舍弃右边取0)
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1010
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1001
原码为 1000 0000 0000 0000 0000 0000 0000 0000 0110
还原回原码是 -6
2.右移操作符>>
右移采用的算术右移还是逻辑右移是取决于编译器的,两种方式都是不一样的的
对于正数而言这两种方式都无伤大雅 ,结果都是一样的,因为正数符号位是0
所以为了检测下面两种结果,我们赋值一个负数来验证
-
逻辑移位
移位规则:左边用0填充,右边丢弃
-
算术移位
移位规则:左边用原该值的符号位填充,右边丢弃
我用的vs编译器来验证的
如果说
-5
原码为 1000 0000 0000 0000 0000 0000 0000 0101
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1010
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1011
-5 >> 1
补码向右整体移一位
算术移位话
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1101(左边用原该值的符号位填充,右边丢弃)
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1100
原码为 1000 0000 0000 0000 0000 0000 0000 0011
结果应该是-3
逻辑移位
补码为 0111 1111 1111 1111 1111 1111 1111 1111 1101(左边用0填充,右边丢弃)
反码为 0111 1111 1111 1111 1111 1111 1111 1111 1100
原码为 0000 0000 0000 0000 0000 0000 0000 0011
结果应该是3
int main()
{
int num1 = -5;
int num2 = num1 >> 1;
printf("num1 == %d num2 ==%d",num1,num2);
return 0;
}
所以我们看出在vs编译器中采用的是算术右移的方式,当然绝大多数编译器都采用的是算术右移的方式进行操作
当然不要移动负数位,这个是标准未定义的---不允许的
int num = 10;
num>>-1;//error
四、位操作符
位操作符一般作用于两个操作数之间,有是通过补码进行操作
注:他们的操作数必须是整数。
1.& 按位与
规则:只要有0就是0,都为1才是1
int main()
{
int num1 = -5;
int num2 = 3;
int num3 = num1 & num2;
printf("num3 == %d",num3);
return 0;
}
3
原码为 0000 0000 0000 0000 0000 0000 0000 0011
反码为 0000 0000 0000 0000 0000 0000 0000 0011
补码为 0000 0000 0000 0000 0000 0000 0000 0011
-5
原码为 1000 0000 0000 0000 0000 0000 0000 0101
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1010
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1011
3 & -5
3补码为 0000 0000 0000 0000 0000 0000 0000 0011
-3补码为 1111 1111 1111 1111 1111 1111 1111 1011
3 & -5补码 0000 0000 0000 0000 0000 0000 0000 0011
3 & -5反码 0000 0000 0000 0000 0000 0000 0000 0011
3 & -5原码 0000 0000 0000 0000 0000 0000 0000 0011
值为3
当然还有一个拿 按位与 操作符可以实现快速统计二进制个数
大家可以看一下我这个文章
2.| 按位或
规则:只要有1就是1,都为0才是0
int main()
{
int num1 = -5;
int num2 = 3;
int num3 = num1 | num2;
printf("num3 == %d",num3);
return 0;
}
3
原码为 0000 0000 0000 0000 0000 0000 0000 0011
反码为 0000 0000 0000 0000 0000 0000 0000 0011
补码为 0000 0000 0000 0000 0000 0000 0000 0011
-5
原码为 1000 0000 0000 0000 0000 0000 0000 0101
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1010
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1011
3 | -5
3补码为 0000 0000 0000 0000 0000 0000 0000 0011
-5补码为 1111 1111 1111 1111 1111 1111 1111 1011
3 | -5补码 1111 1111 1111 1111 1111 1111 1111 1011
3 | -5反码 1111 1111 1111 1111 1111 1111 1111 1010
3 | -5原码 1000 0000 0000 0000 0000 0000 0000 0101
值为-5
3.^ 按位异或
规则:相同为0,相异为1
int main()
{
int num1 = -5;
int num2 = 3;
int num3 = num1 ^ num2;
printf("num3 == %d", num3);
return 0;
}
3
原码为 0000 0000 0000 0000 0000 0000 0000 0011
反码为 0000 0000 0000 0000 0000 0000 0000 0011
补码为 0000 0000 0000 0000 0000 0000 0000 0011
-5
原码为 1000 0000 0000 0000 0000 0000 0000 0101
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1010
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1011
3 | -5
3补码为 0000 0000 0000 0000 0000 0000 0000 0011
-5补码为 1111 1111 1111 1111 1111 1111 1111 1011
3 ^ -5补码 1111 1111 1111 1111 1111 1111 1111 1000
3 ^ -5反码 1111 1111 1111 1111 1111 1111 1111 0111
3 ^ -5原码 1000 0000 0000 0000 0000 0000 0000 1000
值为-8
当然还有一个拿 异或 操作符可以实现不创建临时变量 交换两个整数
A ^ A == 0;
0 ^ A == A;
大家可以看一下我这个文章
五、赋值操作符
1.赋值 =
= 在变量创建的时候给一个初始值叫做舒适化,变量创建好了,再给一个值叫做赋值
int main()
{
int a = 0;//初始化
a = 9;//赋值
printf("a == 9",a);
return 0;
}
赋值以后它的值就改变了
2.复合赋值操作符
比如
+= -= /= %= *= >>= <<=
&= |= ^=
当然这也很简单不难理解,这样写更加简洁。
就相当于是
m + m = m += m m >> m = m >>= m
m - m = m -= m m << m = m <<= m
m / m = m /= m m & m = m &= m
m * m = m *= m m | m = m |= m
m % m = m %= m m ^ m = m ^= m
六、单目操作符
单目操作符顾名思义就是作用于单个操作数
1. ! 逻辑反操作
假如说flag为真 那么!flag就是为假
int main()
{
int flag = 1;
if (flag)
{
printf("真");
}
if (!flag)
{
printf("假");
}
return 0;
}
好比这个代码flag为真进入for语句,!flag为假不进入,结果只打印了一个真
真! = 假
假! = 真
2.- 负值
正常的 数学里面负号
3.+ 正值
正常的 数学里面正号
4.& 取地址
每一个创建的变量都会去开辟一块空间,&取地址操作符就是知道它开辟空间的首地址
int main()
{
int num = 0;
printf("%p",&num);//%p用来打印地址
return 0;
}
5.sizeof 操作数的类型长度
sizeof以字节为单位,sizeof是操作符,不是库函数。
sizeof计算的是变量或类型创建变量的内存大小,和内存里存放什么数据没有关系!!sizeof() 括号里面不能进行计算
#include<stdio.h>
int main()
{
char arr[10] = "abc";
printf("%d\n", sizeof(arr));//该数组10个元素,每个元素1个字节,总大小10*1=10
printf("%d\n", sizeof(a));//a里面有一个元素,占四个字节1*4=4
printf("%d\n", sizeof a);//因为sizeof不是函数,所以对于变量可以不带括号
printf("%d\n", sizeof(int));//整形也是4字节
//printf("%d\n", sizeof int);//但是关键字int外的括号不能省略
return 0;
}
6.~ 对一个数的二进制按位取反
也是一样的针对于补位来操作
int main()
{
int num1 = 0;
int num2 = ~num1;
printf("%d",num2);
return 0;
}
0
原码为 0000 0000 0000 0000 0000 0000 0000 0000
反码为 0000 0000 0000 0000 0000 0000 0000 0000
补码为 0000 0000 0000 0000 0000 0000 0000 0000
~0
补码为 1111 1111 1111 1111 1111 1111 1111 1111 1111
反码为 1111 1111 1111 1111 1111 1111 1111 1111 1110
原码为 1000 0000 0000 0000 0000 0000 0000 0001
值为-1
7.++前置、后置++
这就是一个赋值与运算顺序的问题
前置++ 先自增再赋值
后置++ 先赋值再自增
int main()
{
int a = 1;
int b = ++a;//先自增再赋值
int c = 1;
int d = c++;//先赋值再自增
printf("b = %d,c = %d",b,d);
return 0;
}
8.-- 前置、后置--
这就是一个赋值与运算顺序的问题
前置-- 先自减再赋值
后置-- 先赋值再自减
int main()
{
int a = 1;
int b = --a;//先自减再赋值
int c = 1;
int d = c--;//先赋值再自减
printf("b = %d,c = %d",b,d);
return 0;
}
9.* 解引用操作符
它又叫间接引用操作符
int * p = &a; 把a的地址传到指针p中
对指针p 解引用
*p == a
&a = p
int main()
{
int a = 9;
int* p = &a;
printf("%d",*p);
10.(类型) 强制类型转换
强制类型转换是一种临时的转换
强扭的瓜不甜,强制类型转换万不得已就不用
int main(void)
{
float num = 3.14; //定义了一个浮点型数据
printf("%d",(int)num);//强制转换为整形 ,然后打印
}
七、关系操作符
关系操作符主要作用为判断两个操作数的关系
1.>大于
2.>= 大于等于
3.<小于
4.<=小于等于
5.!= 不等于
6.== 等于
注意!!!!
==等于 跟 = 赋值 ,不一样刚开始很容易将两个弄混,尤其是一些判断语句,刚开始很容易出错
八、逻辑操作符
判断两个操作数的关系
1.&& 逻辑与
就相当于并且,两个条件都为真才为真
#include<stdio.h>
int main()
{
int a = 0;
int b = 5;
int c = a && b;
printf("%d\n", c);;//全真为1,否则为0
return 0;
}
2.|| 逻辑或
就相当于或者,两个条件有一个为真就为真
#include<stdio.h>
int main()
{
int a = 0;
int b = 5;
int d = a || b;
printf("%d\n", d);//有真为1,全假为0
return 0;
}
九、三目操作符
三目操作符(条件操作符)
exp1 ? exp2 : exp3对语句1判断,为真则执行语句2,为假则执行语句3
int main()
{
int a = 1;
a > 2 ? printf("语句1") : printf("语句2");
return 0;
}
a > 2 ? printf("语句1") : printf("语句2");
//就相当于
if(a > 2 )
{
printf("语句1"); //更简洁一点
}
else
{
printf("语句2");
}
十、逗号表达式
exp1,exp2,exp3,…
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
#include<stdio.h>
int main()
{
int a = 1, b = 2;
int c = (a += 1, b += 1, a += 2, a + b);
printf("%d", c);
}
十一、下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值arr[5][ ]操作符,arr跟5是两个操作数
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
函数名 参数都是操作数 ()是操作符
3. 访问一个结构的成员
访问结构体成员用,操作符(左边必须是结构体)
访问结构体指针成员用->操作符(左边必须是结构体变量)
. 结构体.成员名
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
int main()
{
struct Stu stu;
stu.age = 20;//结构成员访问
set_age1(stu);
return 0;
}
-> 结构体指针->成员名
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age2(struct Stu* pStu)
{
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;//结构成员访问
pStu->age = 20;//结构成员访问
set_age2(pStu);
return 0;
}
十二、 隐式类型转换
C 的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为 整型 提升 。
1.整形提升
规则:整形提升是按照变量的数据类型的符号位来提升的
有符号正数补1,有符合负号补0
无符号整形补0
int main()
{
char a = 4;
char b = 127;
char c = a + b;
printf("%d",c);
return 0;
}
3原码0000 0000 0000 0000 0000 0000 0000 0011
127原码0000 0000 0000 0000 0000 0000 0111 1111
a是char类型占8个比特位,把3放进a中发生截断
0000 0011
b是char类型占8个比特位,把127放进b中发生截断
0111 1111
a+b两个整形相加 需要整形提升
所以整形提升的时候,高位补充符号位,即为3提升之后的结果是:0000 0000 0000 0000 0000 0000 0000 0011所以整形提升的时候,高位补充符号位,即为127提升之后的结果是:0000 0000 0000 0000 0000 0000 0111 1111c原码0000 0000 0000 0000 0000 00001000 0000c是char类型占8个比特位,把127放进c中发生截断
10000000
%d打印出来整形再次整形提升,高位补充符号位,即为c
提升之后的结果是:c补码11111111111111111111111110000010(负数的补码转化为原码要转化)c反码1111111111111111111111110000001c原码1000000000000000000001111110结果为-126
2.算术转换
针对大于char类型数据
long doubledoublefloatunsigned long intlong intunsigned intint如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。向上转换
3.操作符的属性
复杂表达式的求值有三个影响的因素。1. 操作符的优先级2. 操作符的结合性3. 是否控制求值顺序果两者的优先级相同,取决于他们的结合性。所以在我看来多用()圆括号将自己的逻辑框起来能避免很多麻烦
创作不易,留个赞咯!!!
希望对你有用