初阶C语言-操作符详解(上)
“不飞则已,一飞冲天;不鸣则已,一鸣惊人。” 今天,我们一起来学习一下操作符的相关知识。
操作符详解
1.操作符分类
算术操作符
+ - * / %
移位操作符<< >>
位操作符& | ^
赋值操作符= += -= *= /=...
单目操作符! sizeof ++ -- ...
关系操作符> >= < <= == !=
逻辑操作符&& ||
条件操作符? :
逗号表达式,
下标引用、函数调用和结构成员[] () ->
2.算术操作符
+ - * / %
注:1.除了
%
操作符之外,其他的几个操作符都可以作用于整数和浮点数。
2.对于/
操作符,如果两个数操作数都为整数,执行整数除法。而只要有浮点数,执行的就是浮点数的除法。
3.%
操作符的两个操作数必须为整数,返回的是整除之后的余数。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int m = 7 / 2;
printf("%dn", m);
double n = 7.0 / 2;
printf("%lfn", n);
int o = 7 % 2;
printf("%dn", o);
return 0;
}
3.移位操作符
>>右移操作符 <<左移操作符
注:移位操作符的操作数只能是整数。
对于一个整数,是4个字节 = 32个比特位。一个整数写成二进制序列的时候,就是32个比特位。对于有符号的整数来说,最高位为符号位,符号位是1表示负数,符号位是0表示正数。
对于无符号数整数来说,没有符号位,所有位都是有效位。
整数的二进制表示形式有3种:原码、反码、补码。
原码:按照数值的正负,直接写出的二进制序列就是原码。
例如:10
这个数的原码
就是00000000000000000000000000001010
,那么-10
的原码就是10000000000000000000000000001010
。
注:对于正的整数,原码、反码、补码相同,无需计算。
对于负的整数,原码、反码、补码是需要计算的。
反码:原码的符号位不变,其他位按位取反。(按位取反,原来是0的改为1,原来是1的改为0)
补码:反码的二进制+1就得到补码。
因此,10
这个数的原码、反码、补码
都是00000000000000000000000000001010
。-10
的原码就是10000000000000000000000000001010
,反码是11111111111111111111111111110101
,补码是11111111111111111111111111110110
。整数在内存中存储的都是补码的二进制序列。 整数在计算的时候,使用的也是补码。
3.1左移位操作符
移位规则:左边抛弃,右边补0
#include <stdio.h>
int main()
{
//m:00000000000000000000000000000111
int m = 7;
int n = m << 1;
//左移位原则:左边抛弃,右边补0
//n:00000000000000000000000000001110
//n = 2+4+8=14
printf("%dn", m);
printf("%dn", n);
return 0;
}
我们可以发现左移位操作符可以起到加倍的作用。负数也是一样的道理:
3.2右移操作符
移位规则(右移运算分两种):
1.逻辑移位:左边用0填充,右边丢弃。
2.算术移位:左边用原该值的符号位填充,右边丢弃。
//右移位操作符
#include <stdio.h>
int main()
{
//a的原码:10000000000000000000000000001010
//a的反码:11111111111111111111111111110101
//a的补码:11111111111111111111111111110110
//大多数编译器采用的是算术右移
//b是算术右移(右边直接丢弃,左边补原符号位,原来是负数,左边补1,原来是正数,左边补0)
//b的补码:11111111111111111111111111111011
//b的反码:11111111111111111111111111111010
//b的原码:10000000000000000000000000000101
//b=-(1+4)=-5
int a = -10;
int b = a >> 1;
printf("a=%dn", a);
printf("b=%dn", b);
return 0;
}
我们可以看到,算术右移也起到了/2
的作用。
注:对于移位操作符,不要移动负数位,这个是标准未定义的。
4.位操作符
位操作符有:
|(按位或)&(按位与)^(按位异或)
注:他们的操作数必须是整数。
4.1按位与&
对于两个数a和b
,用按位与运算符求c = a&b;
,需要先写出a和b
的二进制位的补码形式,然后一一对比,只有当两个数a和b的对应的补码都为1的时候,按位与的结果才是1,否则为0。
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a & b;//按(二进制)位与
//-5的原码:10000000000000000000000000000101
//-5的反码:10000000000000000000000000000100
//-5的补码:11111111111111111111111111111011
// 3的补码: 00000000000000000000000000000011
// 按位与:两个都为1才为1
// c: 00000000000000000000000000000011
//结果为3
printf("%dn", c);
return 0;
}
4.2按位或|
对于两个数a和b
,用按位或运算符求c = a|b;
,需要先写出a和b
的二进制位的补码形式,然后一一对比,只要当两个数a和b的对应的补码有一个为1的时候,按位或的结果就是1,否则为0。
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a | b;//按(二进制)位或
//-5的原码:10000000000000000000000000000101
//-5的反码:10000000000000000000000000000100
//-5的补码:11111111111111111111111111111011
// 3的补码: 00000000000000000000000000000011
// 按位或:有一个为1即为1
// c的补码: 11111111111111111111111111111011
// c的反码: 11111111111111111111111111111010
// c的原码: 10000000000000000000000000000101
//结果为-5
printf("%dn", c);
return 0;
}
4.3按位异或^
对于两个数a和b
,用按位或运算符求c = a^b;
,需要先写出a和b
的二进制位的补码形式,然后一一对比,两个数a和b的对应的补码相同为0,不同为1.
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;//按(二进制)位异或
//-5的原码:10000000000000000000000000000101
//-5的反码:10000000000000000000000000000100
//-5的补码:11111111111111111111111111111011
// 3的补码: 00000000000000000000000000000011
// 按位异或:相同为0不同为1
// c的补码: 11111111111111111111111111111000
// c的反码: 11111111111111111111111111110111
// c的原码: 10000000000000000000000000001000
//结果为-8
printf("%dn", c);
return 0;
}
注:特别地,a^a=0; a^0=a;
.
下面,我们给出一个特别的题:在不创建临时变量的情况下(没有第三个变量),实现两个数的交换。
通过前面的学习,我们知道在使用第三个变量是怎么实现的了:
//实现两个数的交换
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = 0;
printf("交换前a=%d,b=%dn", a, b);
c = a;
a = b;
b = c;
printf("交换后a=%d,b=%dn",a,b);
return 0;
}
那么,如果我们不使用第三个变量,该如何实现同样的功能呢?
//实现两个数的交换
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("交换前a=%d,b=%dn", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("交换后a=%d,b=%dn", a, b);
return 0;
}
这里,我们只用了两个变量,但是还是存在缺陷,就是当a和b
的值很大的时候,就会出现溢出的问题。这里,我们就需要用到异或^
这个操作符了。
//实现两个数的交换
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("交换前a=%d,b=%dn", a, b);
a = a ^ b;//a=3^5;
b = a ^ b;//b=3^5^5=3
a = a ^ b;//a=3^5^3=3^3^5=5
//异或支持交换律
printf("交换后a=%d,b=%dn", a, b);
return 0;
}
注:异或^
不存在溢出的问题,但是可读性不高,只限于整型的运算。
5.赋值操作符
赋值操作符用于在变量创建好之后对值的修改。
#include <stdio.h>
int main()
{
int a = 3;//不是赋值,是初始化
a = 10;//赋值
return 0;
}
赋值操作符可以连续使用,比如:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = a = a + b;//连续赋值
//连续赋值从右往左算
//a = a + b = 30
//c = a = 30
printf("%dn", c);
return 0;
}
但是,连续赋值的语句不利于调试,上述的int c = a = a + b;
可以拆分成:
a = a + b;
c = a;
//这样的写法更清晰爽朗且易于调试。
复合赋值符:
+= -= *= /= %= >>= <<= &= |= ^=
这些运算符都可以写成复合的效果,例如:
int a = 2;
//以下两种写法表达含义一致
a = a + 10;
a += 10;
6.单目操作符
6.1单目操作符的介绍
单目操作符 | 名称 |
---|---|
! | 逻辑反操作符 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
- - | 前置、后置- - |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
1.逻辑反操作符(!)
//0表示假,1表示真
#include <stdio.h>
int main()
{
int a = 1;
int b = 0;
b = !a;
printf("%dn", b);
return 0;
}
2.解引用操作符(*)
//解引用操作符*
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;
*p;//对p解引用操作,*p是通过p中存放的地址,找到p指向的对象
//*p就是a
return 0;
}
3.sizeof和数组
//sizeof计算的是类型创建变量或变量的大小,单位是字节
//sizeof计算的结果是size_t类型的
//size_t是无符号的
//对size_t类型的数据进行打印,可以用%zd
//sizeof后面的括号在括号中写的不是类型的时候,括号可以省略,这样也说明了sizeof不是函数
//sizeof是操作符-单目操作符
#include <stdio.h>
int main()
{
int a = 10;
printf("%zdn", sizeof(a));
printf("%zdn", sizeof a);
printf("%zdn", sizeof(int));
int arr[10] = { 0 };
printf("%zdn", sizeof arr);
printf("%zdn", sizeof(arr[1]));
return 0;
}
4.~(对一个数的二进制按位取反)
#include <stdio.h>
int main()
{
int a = 0;
printf("%dn", ~a);
//a的补码: 00000000000000000000000000000000
// 按位取反,就是不论是不是符号位都取反
//~a的补码:11111111111111111111111111111111
//~a的反码:11111111111111111111111111111110
//~a的原码:10000000000000000000000000000001
// 符号位0为正 1为负
//~a的值为-1
return 0;
}
那么,接下来,可以看看~
怎么用?
#include <stdio.h>
int main()
{
int a = 10;
//000000000000000000000000000001010
//如果我们想让从右往左数第三个数变成1该怎么做?
// 即变成00000000000000000000000000001110为14
//将000000000000000000000000000000001左移二
//1<<2 00000000000000000000000000000100
//将左移后的二进制位与a的二进制位按位或即可
a |= (1 << 2);
printf("%dn", a);
//那么,反过来,如果希望从右往左数第三个数再变为0该怎么办?
// 只需要将下面第一个和第二个按位与就能得到第三个
//0000000000000000000000000000001110
//1111111111111111111111111111111011,这个可以将(1<<2)取反得到
//0000000000000000000000000000001010
a &= ~(1 << 2);
printf("%dn", a);
return 0;
}
5.++ --
操作符
#include <stdio.h>
int main()
{
int a = 2;
//++a;
//++是一种自增1的操作
a++;
//前置++和后置++对于a本身没有影响,都是进行+1的操作
printf("%dn", a);
return 0;
}
前置++和后置++对于a本身没有影响但是对于别人有影响!示例如下:
#include <stdio.h>
int main()
{
int a = 2;
int b = a++;
//后置++:先使用,后+1
printf("%dn", a);
printf("%dn", b);
return 0;
}
#include <stdio.h>
int main()
{
int a = 2;
int b = ++a;
//前置++:先+1,后使用
printf("%dn", a);
printf("%dn", b);
return 0;
}
前置--和后置--和前、后置++同理,大家可以自己试一下!
好啦,关于操作符的知识点就先讲到这里,后期会继续更新,欢迎大家持续关注、点赞和评论!