C语言操作符详解(一篇文章教你搞懂C语言的操作符)

在这里插入图片描述

准备知识

数据在内存中是以二进制补码的形式存储

  1. 十进制是逢十进一,二进制也就是逢二进一,在计算机电路中也就是电路的开与关
    比如十进制的13,在计算机中会转换成二进制1101
  2. 一个数据在计算机中有原码、反码、补码三种形式,我们输入一个数,或者给一个变量赋值,都是以原码的形式存入,并通过原码转换成反码再转换成补码,最终以补码的形式存储到内存中,在表达式求值、,用的是补码的形式进行处理
    正数和负数的转换方式不同:正数的原码、反码、补码完全相同,负数的反码是符号位不变,其他位按位取反,就是反码,补码就是反码的基础上+1
  3. 一个数分为有符号数和无符号数,有符号数的存储最高位是符号位,剩下的位是数字位,正数的符号位为0,负数的符号位为1,无符号数所有位都是数字位。
int a = 2;
//a是正数,并且是整形,整形占四个字节/三十二个比特位,最高位是符号位,正数符号位是0
//00000000000000000000000000000010 - 原码
//00000000000000000000000000000010 - 反码
//00000000000000000000000000000010 - 补码
//在内存中a就是以补码的形式存储

int b = -5;
//b是负数,并且是整形,最高位是符号位,负数符号位是1
//10000000000000000000000000000101 - 原码
//11111111111111111111111111111010 - 反码
//11111111111111111111111111111011 - 补码
//内存中b就是以补码的形式存储

在这里插入图片描述

操作符

移位操作符>> <<

//使用形式:
int a = 3;
int b = a<<2;//a向左移动两位
int c = a>>3;//a向右移动三位
  1. 左移运算符<<
    图像演示向左移动一位:
    在这里插入图片描述
    ps:移动的位数需要大于等于0,小于等于31

  2. 右移运算符>>
    右移运算符有两种,第一种是算数右移(右边丢弃,左边补原来符号位上的数),第二种是,逻辑右移(右边丢弃,左边补00,采用哪一种是根据编译器来决定,目前大多数编译器都是采用第一种——算数右移
    在这里插入图片描述

位操作符& | ^

位操作都是两目运算符,并且两个操作数都需要是整数

  1. 按位与 &
    将左右两个操作数变成二进制位,用补码进行运算,两个操作数对应位都为1结果才为1,有0则为0
    在这里插入图片描述
    上式中c的结果为3

  2. 按位或 |
    将左右两个操作数变成二进制位,用补码进行运算,两个操作数对应位有1则为1,没1才为0
    在这里插入图片描述
    上式结果为-5

  3. 按位异或 ^
    将左右两个操作数变成二进制位,用补码进行运算,两个操作数对应位相异为1,相同为0
    在这里插入图片描述
    异或的性质:
    1、a ^ a = 0
    2、0 ^ a = a
    3、a ^ b = b ^ a (交换律)
    4、(a ^ b) ^ c = a ^ (b ^ c) (结合律)
    位操作符的运用:
    不创建新的变量交换两个数的值

int a = 2;
int b = 3;

a = a ^ b;//a与b异或,然后放在a中
b = a ^ b;//实际这里a与b异或的意思是,(a^b)^b,也就等于a^(b^b),也就等于a^0,也就是a,所以其结果就是a
a = a ^ b;//实际这里a与b异或的意思是,(a^b)^a,根据性质,可知结果为b,即完成交换

单目运算符 ~

对一个数的二进制序列按位取反(包括符号位)

int a = 0
printf("%d\n", ~a);//-1
//  a -> 00000000000000000000000000000000
// ~a -> 11111111111111111111111111111111  - 补码,输出的时候需要还原回原码

运用

运用以上的运算符进行一些二进制序列处理:

//要求1:让a的二进制序列的第五位变成1,其他位保持不变,
int a = 9;//00000000000000000000000000001001   9
a |= (1<<4);
printf("%d\n", a);//00000000000000000000000000011001   25
//要求2:再从25,变回9(第五位变成0,其他位不变)
a ^= (1<<4);
printf("%d\n", a);//00000000000000000000000000001001   9

单目操作符++ --.

int a = 3, b = 5, c = 2, d = 1;
a++;
++b;
c--;
--d;
printf("%d\n", a);//4
printf("%d\n", b);//6
printf("%d\n", c);//1
printf("%d\n", d);//0

不管是前置++/-- 还是后置++/-- 其本质都是a = a + 1/a = a - 1
前置++ 先++,后使用
后置++ 先使用,后++
在例子中感受‘使用’的意思

int a = 10;
int b = a++;
printf("%d\n", a);//11
printf("%d\n", b);//10,a先赋给了b,然后再+1
int a = 10;
int b = ++a;
printf("%d\n", a);//11
printf("%d\n", b);//11,a先+1,再赋给了b

int a = 10;
printf("%d\n", a++);//10,a先进行了输出,再+1
printf("%d\n", a);//11

void test(int b)
{
    printf("%d", b);//10
}
int a = 10;
test(a++);//先传值,再+1
printf("%d\n", a);//11

逻辑运算符&& ||

区别:逻辑与&&和逻辑或||是针对于多个表达式之间的关系,按位与&和按位或 | 是针对两个操作数在位上的关系

  1. 逻辑与: - 串联
    a && b
    真 && 真 -> 真
    真 && 假 -> 假
    假 && 真 -> 假
    假 && 假 -> 假

  2. 逻辑或: - 并联
    a || b
    真 || 真 -> 真
    真 || 假 -> 真
    假 || 真 -> 真
    假 || 假 -> 假

int a = 0, b = 0, c = 1, d = 1;
printf("%d\n", a&&b);// 0
printf("%d\n", a&&c);// 0
printf("%d\n", c&&a);// 0
printf("%d\n", c&&d);// 1

printf("%d\n", a||b);// 0
printf("%d\n", a||c);// 1
printf("%d\n", c||a);// 1
printf("%d\n", c||d);// 1

逻辑与和逻辑或的短路现象
在这里插入图片描述
&&操作符在发现左边的表达式为0时,后面就不会进行运算了,a本身加了1,i直接得到结果为0, 因此a = 1 b = 2 c = 3 d = 4
在这里插入图片描述
||操作符发现左边的表达式为1时,后面的也不会进行运算了,a本身加了1,i得到结果为1, 因此a = 2 b = 2 c = 3 d =4
在这里插入图片描述
||操作符发现a为0,还无法判断出结果,因此还要计算++b,a本身加了1,b本身也加了1, 因此 a= 1 b = 3 c = 3 d = 4

总结:&&的停止是需要寻找假,因为&&是遇假则假,全真才真,同理,||的停止是需要寻找真

逻辑与和逻辑或的短路是为了提高运算效率,但是这样也有缺陷
在这里插入图片描述
因此在进行表达式的逻辑与和逻辑或时,可以提前进行赋值和计算,并用()来表示思维逻辑,防止编译器错误的解读我们的表达

三目操作符 ? :

exp1?exp2:exp3
如果exp1为真,那么执行exp2,反之,执行exp3

其实 ?: 就是一个简单的if-else语句,这是这样的话更加简洁,高效

int a = 2;
int b = 3;
printf("%d\n", (a > b) ? a : b);//3
printf("%d\n", (a < b) ? a : b);//2

逗号表达式

exp1, exp2, exp3, …
从左至右依次执行表达式,整个表达式的结果为最后一个表达式的结果

int a = 3;
int b = 2;
printf("%d\n", (a = b + 2,++a));//5

下标引用操作符 [ ]

int arr[10] = { 1,2,3,4,5 };
printf("%d\n", arr[4]);//arr[4]这个表达式中[]有两个操作数 arr和4

表达式求值

我们以上讲解的所有操作符就是为了我们进行表达式求值
除了上述表达式本身的运算意义,我们还需要了解影响表达式求值的另外的因素

类型转换

类型转换分为显式类型转换和隐式类型转换

显式类型转换

显式类型转换的具体操作就是强制类型转换:
(类型)变量
强制类型转换是把变量从一种类型转换为另一种数据类型,这种改变是临时的,也就是说,只是在表达式求值过程中的一种类型变化,变量本身并未真正改变,一般用于取整,或者精度控制

double a = 3.14;
int b = (int)a;
printf("%lf\n", a);//3.140000
printf("%d\n", b);//3

隐式类型转换

  1. 整形提升
    表达式中的字符型char和短整型short的变量在使用前会转换为普通整形int,如果是有符号数,符号位为1补1,符号位为0补0,一直补到32位,无符号类型就补0
    在这里插入图片描述
    解读:
    char a = 3 —3本身是整形,也就是00000000 00000000 00000000 00000011,我们想放入char类型的a中,那么就会发生截断,截去所有超出的高位,保留低位,a中存放的是00000011
    在这里插入图片描述
    char b = 127 —127的二进制序列为00000000 00000000 00000000 01111111,截断后,b中为01111111
    a + b,a和b都是char类型,首先进行整形提升,a和b符号位都是0,分别变成
    00000000 00000000 00000000 00000011
    00000000 00000000 00000000 011111111
    进行ab加和
    得到
    00000000 00000000 00000000 10000010
    c是char类型,因此发生截断,c中存储的是10000010,注意这里存储的补码
    在printf时,c是char类型,首先进行整形提升,符号位是1,因此补1,
    11111111 11111111 11111111 10000010 转换成原码:
    10000000 00000000 00000000 01111110
    用%d整形打印,得到-126

在这里插入图片描述
执行 a == 0xb6时,a会发生整形提升,0xb6本身是整形,不需要提升,a整形提升补的是1,因此不相等(如果a的最高位是0,那整形提升就不会改变值),c == 0xb6000000,c和后者都是整形,不会发生整形提升,因此相等

  1. 算数转换
    当操作数之间类型不同,那么会向精度高的操作数的类型转换
    long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int
int a = 3;
double b = 3.14;
printf("%d\n", a + b);//6.140000

在a + b之前,a会自动转换成double形

操作符属性

  1. 操作符的优先级
  2. 操作符的结合性(从左至右,还是从右至左)
  3. 是否控制求值顺序( && || ?: , 就这四个操作符存在控制求值顺序)

两个相邻的操作符的执行顺序:
第一,取决于他们的优先级,
第二,在优先级相同时,取决于他们的结合性

有了这两条规则,很多时候的表达式求值计算路径还是不唯一
例子:

c + --c;
//到底是先用原来的c,再--c,还是先--c,再用c
int main()
{
    int i = 10;
    i = i-- - --i * (i = -3) * i++ + ++i;
    printf("%d\n", i);
}
//上述代码在不同的编译器上得到的答案基本都不同

因此,在表达式求值时,多用小括号表达逻辑,或者分成多条语句进行运算,避免计算路径不唯一导致的结果出错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失去梦想的小草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值