前言
本篇分享的是部分操作符的概念与用法,从经典例题入手,带你快速了解和掌握。
收录专栏:浅谈C语言
操作符详解上
- 1. 操作符分类
- 2. 算术操作符
- 3. 移位操作符
- 3.1 左移操作符
- 3.2 右移操作符
- 4. 位操作符
- 5. 赋值操作符
- 6. 单目操作符
- 6.1 单目操作符介绍
- 6.2 sizeof 和 数组
- 7. 关系操作符
- 8. 逻辑操作符
- 9. 条件操作符
1. 操作符分类
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
2. 算术操作符
包括 + - * / %
例如:计算 3/2
#include<stdio.h>
int main()
{int a=3/2;printf("%d\n",a)return 0;
}
如果你觉得这样写,就大错特错了。
🍤 运行结果:
这是因为这里的 a 是整型的,计算出的结果就是整型,会自动舍去小数点后的数字。
更改 a 的类型为浮点数:
int main()
{double a = 3 / 2;printf("%lf\n", a);return 0;
}
其实,这样也是不对的。
🍤 运行结果:
仔细观察就会发现,这里的 a 虽然是 double 类型的,但 3 和 2 都是整形的, 3/2 计算后是 1,然后再将 1 装换成 double 型的赋给 a。
所以当计算的结果会出现浮点数时,除号的两端只要有一个小数就可以。
int main()
{//下面三种方式都可以计算出 1.5double a = 3.0 / 2.0;//double a = 3.0/2;//double a = 3 / 2.0;printf("%lf\n", a);return 0;
}
注:
🍥 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数
🍥 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法
🍥 除法中,除数不可以为 0
🍥 % 操作符的两个操作数必须为整数。返回的是整除之后的余数
3. 移位操作符
<< 左移操作符>> 右移操作符
移位操作符的操作数只能是整数。
移动的是二进制,因为能够处理的是二进制的信息
3.1 左移操作符
移位规则:左边抛弃、右边补0
例如:
#include<stdio.h>
int main()
{int a = 15;printf("%d\n",a<<1);//移动就是a中的2进制信息return 0;
}
🍤 图解:
🍤 运行结果:
3.2 右移操作符
移位规则:
首先右移运算分两种:
- 逻辑移位
左边用0填充,右边丢弃- 算术移位
左边用原该值的符号位填充,右边丢弃
在C语言没有明确规定倒是算术右移还是逻辑右移,一般编译器上采用的是算术右移。
例如:
int a = -1;
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int a = 15;
a>>-1;//error
移位移动的是补码的二进制序列
//tip:
int a = 15;//00000000000000000000000000001111 - 原码//00000000000000000000000000001111 - 反码//00000000000000000000000000001111 - 补码int b = -15;//10000000000000000000000000001111 - 原码//11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码)//11111111111111111111111111110001 - 补码(反码+1就是补码)//整数在内存中存储的是补码//计算的时候也是使用补码计算的
4. 位操作符
位操作符有:(也是操作二进制位)
& //按位与
| //按位或
^ //按位异或
🍩上述的操作数必须是整数。
例如:
// & 操作符
#include<stdio.h>int main()
{int a = 3;//00000000000000000000000000000011 - 补码int b = -5;//10000000000000000000000000000101//11111111111111111111111111111010//11111111111111111111111111111011 - 补码int c = a & b;//& -- 对应二进制位有0则为0,两个同时为1,才是1//00000000000000000000000000000011//11111111111111111111111111111011//00000000000000000000000000000011 - 补码printf("%d\n", c);//3return 0;
}
// | 操作符
#include<stdio.h>
int main()
{int a = 3;//00000000000000000000000000000011 - 补码int b = -5;//10000000000000000000000000000101//11111111111111111111111111111010//11111111111111111111111111111011 - 补码int c = a | b;// | - 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0//00000000000000000000000000000011//11111111111111111111111111111011//11111111111111111111111111111011 - 补码//11111111111111111111111111111010//10000000000000000000000000000101 - -5printf("%d\n", c);//-5return 0;
}
// ^ 操作符
#include<stdio.h>
int main()
{int a = 3;//00000000000000000000000000000011 - 补码int b = -5;//10000000000000000000000000000101//11111111111111111111111111111010//11111111111111111111111111111011 - 补码//int c = a ^ b;//^ - 按二进制位异或 -对应的二进制位相同为0,相异为1//00000000000000000000000000000011//11111111111111111111111111111011//11111111111111111111111111111000 - 补码//11111111111111111111111111110111//10000000000000000000000000001000 - -8printf("%d\n", c);//-8return 0;
}
实例1:交换2个整型变量
要想交换两个变量的值,最常见的方法是再创建一个变量,帮助我们实现:
#include<stdio.h>
int main()
{int a = 3;int b = 5;//交换int tmp = a;//临时变量 tmpa = b;b = tmp;printf("交换前:a=%d b=%d\n", a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
🍤 运行结果:
如果不创建临时变量(第三个变量),可以实现两个整数的交换吗?
回答是可以的
方法一:
#include<stdio.h>
int main()
{int a = 3;int b = 5;a = a + b;b = a - b;a = a - b;printf("交换前:a=%d b=%d\n", a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
🍤 运行结果:
方法二:使用 ^ 操作符实现
#include<stdio.h>
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;
}
🍤 运行结果:
异或操作符也支持交换律
例如:
//a^a -> 0
//a^0 = a
#include<stdio.h>int main()
{int a = 3;int b = 5;int c= a^b^a;int d= a^a^b;printf("%d %d\n", c, d);return 0;
}
🍤 图解:
a ^b ^a=b
同样地,a^a^b=a
🍤 运行结果:
实例2:求一个整数存储在内存中的二进制中1的个数。
//方法1
#include <stdio.h>
int main()
{int num = 10;int count = 0;//计数while (num){if (num % 2 == 1)count++;num = num / 2;}printf("二进制中1的个数 = %d\n", count);return 0;
}
//方法2:
#include<stdio.h>
int main()
{int num = -1;int i = 0;int count = 0;//计数for (i = 0; i < 32; i++){if (num & (1 << i))count++;}printf("二进制中1的个数 = %d\n", count);return 0;
}
//方法3,优化版本
#include <stdio.h>
int main()
{int num = -1;int i = 0;int count = 0;//计数while (num){count++;num = num & (num - 1);}printf("二进制中1的个数 = %d\n", count);return 0;
}
5. 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续使用
例如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1;
a = x;
可见,下面的写法更加清晰爽朗而且易于调试。
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。
例如:
int x = 10;
x = x+10;
x += 10;//复合赋值,与上面等式等价
//但下面复合语句明显更加简洁。
其他运算符也是一样的道理。
6. 单目操作符
6.1 单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
🍩单目操作符只有一个操作数
实例1: !
// ! 操作符
//非 0 为真
#include<stdio.h>
int main()
{int flag = 5;if (flag == 0){printf("hehe\n");}if (!flag)//这里当 flag 为假时,才会打印hehe{printf("hehe\n");}if (flag){printf("haha\n");}return 0;
}
🍤 运行结果:
实例2: -
// - 操作符
#include<stdio.h>
int main()
{int a = 5;int b = -a;printf("%d\n", b);return 0;
}
🍤 运行结果:
实例3: &
// & * 操作符
#include<stdio.h>
int main()
{int a = 10;//pa是指针变量int* pa = &a;//&-取地址操作符-取出a的地址*pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容)//同时也更改了a 的内容int c = *pa;printf("a=%d c=%d\n",a, c);return 0;
}
🍤 运行结果:
实例4:在计算数据类型所占内存大小时,可以使用 sizeof 操作数
🍥 sizeof不是函数,是操作符
🍥 sizeof计算的是类型创建变量的大小,单位是字节
//sizeof 操作符:
#include <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));printf("%d\n", sizeof a);//printf("%d\n", sizeof int);//不允许这样计算,说明 sizeof 不是函数return 0;
}
实例5: ~
// ~ 按补码二进制位取反
#include<stdio.h>
int main()
{int a = 0;printf("%d\n", ~a);//00000000000000000000000000000000//11111111111111111111111111111111 - 补码//11111111111111111111111111111110//10000000000000000000000000000001 -1return 0;
}
🍤 运行结果:
实例6:前置 ++ 、 后置 ++
//前置++:先对变量进行自增,然后使用该变量
int main()
{int a = 1;int b = ++a;//前置++,先++,后使用//a=a+1,b=aprintf("a=%d b=%d\n", a, b);//2 2return 0;
}
🍤 运行结果:
//后置++:先对a先使用,再自增
#include<stdio.h>
int main()
{int a = 1;int b = a++;//后置++,先使用,后++//b=a,a=a+1printf("a=%d b=%d\n", a, b);//2 1return 0;
}
🍤 运行结果:
前置--、后置--等同
6.2 sizeof 和 数组
实例:
#include <stdio.h>
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;
}
🍤 运行结果:
7. 关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
🍩编写过程中 == 和 = 不要写错
实例:判断闰年
#include<stdio.h>
int main()
{int y = 0;scanf("%d", &y);//1. 能被4整除,并且不能被100整除//2. 能被400整除是闰年if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)){printf("闰年\n");}else{printf("不是闰年\n");}return 0;
}
8. 逻辑操作符
&& 逻辑与--并且
|| 逻辑或--或者
1&2----->0
1&&2---->1
// & 两边二进制补码计算,相同为1,不同为0
// && 两边同为真,结果也是真,用 1 表示;只要有一个为假,结果就是假,用 0 表示
1|2----->3
1||2---->1
// | 两边二进制补码进行计算,只要有一个 1 就是 1;当两边都为 0 时,才是0
// || 两边只有一个真时,就为真;都是假时,才为假
实例1:
// &&
#include <stdio.h>
int main()
{int i = 0, a = 0, b = 2, c = 3, d = 4;i = a++ && ++b && d++;printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);return 0;
}
🍤 运行结果:
实例2:
// ||
#include <stdio.h>
int main()
{int i = 0, a = 0, b = 2, c = 3, d = 4;i = a++||++b||d++;printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);return 0;
}
🍤 运行结果:
9. 条件操作符
exp1 ? exp2 : exp3
实例:
//如果 a>5,则 b=3
//如果 a<=5,则 b=-3
if (a > 5)b = 3;
elseb = -3;
上述代码可以转换成条件表达式:
#include <stdio.h>
int main()
{int a ;scanf("%d", &a);int b = a > 5 ? 3 : -3;printf("%d\n", b);return 0;
}
🍤 运行结果:
使用条件表达式实现找两个数中较大值
#include <stdio.h>
int main()
{int a, b ;scanf("%d %d", &a, &b);int c = a > b ? a : b;printf("%d\n", c);return 0;
}