操作符
算术操作符/
当 进行7/2时答案只会是3,将7换成7.0或2换成2.0,或者两者都换成小数结果就是3.5。
移位操作符
整型的二进制表示:
原码,反码,补码
符号位为0,表示正数,符号位为1表示负数。(符号位在最高位,也就是第一位)
正整数
正整数的原码、反码、补码都相同

负数
原码变反码:原码的符号位不变,其他位按位取反
反码变补码:反码+1

左移操作符<<

把a的2进制位写出来,左边丢弃右边补0

这时可得a的大小由-4变成了-8
a的三种码:

右移操作符>>
1、逻辑右移
右边丢弃,左边补0
2、算数右移
右边丢弃,左边补原符号位
绝大部分编译器右移采用算数右移
对于移位操作符,不要移动负数位,这是标准未定义的

位操作符
& 按位与
两个2进制序列中,如果有0则为0,两个同时为1才为1。
第3行就是前两行按位与的结果

| 按位或
两个2进制序列中,如果有1则为1,两个同时为0才为0。
第3行是前两行按位或的结果

^ 按位异或
异或的运算规律,相同为0,相异为1。
第3行是前两行按位异或的结果

小练习
不能创建临时变量(第3个变量),实现两个数的变换
这种方法固然可行,但当a ,b的数值过大时,就不太适用

我们今天学习操作符,或许可以用操作符来解决这个问题
就像 3^3
5^5
他们都数字相同,所以异或的结果也都是0.
3^5 结果是6 110
如果我们这样操作3^5^5:
5^5的结果是0,我们又可以发现3^0的结果还是3
所以 3^5^5的结果就是3
则3^5^3 的结果就是5
我们就可以把3^5看做密码,3、5任意一个数与密码异或就会得到另一个数。
那么这道题就迎刃而解:
#include<stdio.h>
int main()
{
int a = 9;
int b = 6;
a = a ^ b;
b = b ^ a;
a = a ^ b;
printf("%d %d \n", a, b);
return 0;
}
1代码指的是讲密码赋值给a
2代码指的是将原来的b与赋值后的a异或再赋值给b,结果就会是原来的a
3代码指的是将赋值后的b(值为原来的a)与密码相异或得到b房,赋值给a
这个是1代码

这个是2代码

这个是3代码

赋值操作符


复合赋值符

单目操作符

! 逻辑反操作

这里的flag = 0,为假,!flag就为真
& 取地址符号

int*pa = &a 就是把a的地址存放在pa里面
*pa 叫解引用,通过a的地址找到a,也可以通过改变pa的值来改变a的值
只存在 *pa = 2 不存在pa = 2
这种给100强制分配一个空间,这种指针属于野指针

~按位取反
把变量数值的补码按位取反

第一行是a 的补码,第二行是~a的补码,第三行是~a的反码(补码-1),第四行是~a的原码(反码符号位不变,其他位按位取反)
我们观察这段二进制序列,现在的需求是将第三位的0换成1,且其他位不变。

这时只需按位或一个第二行的这种序列就可以实现

第二行也可以表示为1向左移动2位
表示为 a = a | ( 1<<2)
当我们需要设置第n位时就可以表示为1向左移动(n-1)位
![]()
改为0的代码类型
把a的第n位置改为0 a = a &~(1
前置与后置++ --



sizeof
求变量(类型)所占空间的大小。

这种sizeof后面放类型也省略的做法是错误的

我们再来观察这一段代码

这里的a+5打印出来的结果为什么会是2?
a 是int类型,a+5也同样是int类型,但s却是short类型,只有2字节的空间,但必须要把int类型的值放在short类型的变量中去,这时就会发生“截断”(把4个字节砍掉2个字节)再放进去,这样打印的就是short类型就是2.
sizeof()内部放的表达式不计算的
关于如何截断

上面是2的二进制位,中间是5的二进制位,下面是两个加起来的二进制位,将他们加起来的数需要进行截断,取得当然是低的字节,否则得出来的数永远不准确
就是这样

我们再观察这段代码

这里的(2)(4)打印的结果都是4
(2)的指针是int类型的,(4)的指针是char*类型的可能有人会觉得char*类型的指针打印出来的结果就是2,其实不然。
所有指针类型大小都是固定的4字节。(前提是在x86环境下,x64环境就是8字节)

(类型)
类型强制转换

关系操作符


逻辑操作符


注意,上强度了
这段代码

& 的规则:有0就为0,两个同时为1,才为1
这里的a++是0,0为假,++b就不用算,也为假 a++ && ++b,整体都为假了,后面也不用算了。
所以结果就是 1 2 3 4
在看下这段代码

结果显而易见是 2 3 3 5
再看这段代码

结果还是2234

1334
三目操作符

可以改成

逗号表达式
从左向右依次计算
这个代码就是前面算的值赋给d,最后再算d是否 >0

这个代码左边有点冗余,换成右边就刚刚好

下标引用、函数调用和结构成员
下标引用 []
概念:
用于访问数组或容器中的指定位置元素。
本质上是通过指针偏移来取值。
示例:
arr[2] // 访问数组第 3 个元素
函数调用 ()
概念:
用于调用函数并传入参数,执行函数体并返回结果。
示例:
add(3, 4) // 调用函数 add,并传入参数 3 和 4
结构成员访问 . 和 ->
.(点)概念:
用于通过对象或结构体变量访问其成员。
->(箭头)概念:
用于通过指向对象的指针访问其成员。
s.name // 通过对象访问成员
p->name // 通过指针访问成员(等价于 (*p).name)
算术转换
整型提升
整型提升是按照变量的数据类型的符号位来提升的
这里的a b 计算前都将二进制序列进行了截断,随后再相加

要进行计算时就需要整型提升,补充符号位,这里两个数的符号位都为0 ,所以就补0,如果符号位是1就会全补1

讲2者相加后值赋给c,但c是char类型所以需要截断

接下来我们要进行打印
但%d打印的是十进制的有符号整数

这里的c不够,我们就需要整型提升得补码,-1得反码,再按位取反得原码

再看这段代码

打印出来的结果是只有c,因为a b 应该char 类型 一个short 类型,打印就需要整型提升,提升后与原数值不符

无符号的整型提升,高位直接补0
再来看这段代码的运行结果

只有第一个结果是1,因为这里的c只要是表达式就会涉及运算,就会涉及整型提升,无论是否有意义。
转换优先级
当两个类型不一样,转换方向总是向上转成更宽的类型:
| 类型等级 | 类型 |
|---|---|
| 1 | long double |
| 2 | double |
| 3 | float |
| 4 | unsigned long long |
| 5 | long long |
| 6 | unsigned long |
| 7 | long |
| 8 | unsigned int |
| 9 | int(包含 char/short) |
操作符的属性
1、操作符的优先级
2、操作符的结合性
3、是否控制求值顺序

像第一个两个相邻的操作符,先算优先级高的操作符,而第二个就不确定了

操作符的优先级
| 优先级 | 操作符 | 含义 | 结合性 |
|---|---|---|---|
| 1 | () [] -> . ++ -- | 括号调用、数组下标、成员访问、后缀自增/自减 | 左到右 |
| 2 | ++ -- + - ! ~ * & (type) sizeof | 单目运算符、强转、取地址、逻辑/位非等 | 右到左 |
| 3 | * / % | 乘法、除法、取模 | 左到右 |
| 4 | + - | 加减 | 左到右 |
| 5 | << >> | 位移 | 左到右 |
| 6 | < <= > >= | 比较大小 | 左到右 |
| 7 | == != | 相等/不等 | 左到右 |
| 8 | & | 按位与 | 左到右 |
| 9 | ^ | 按位异或 | 左到右 |
| 10 | ` | ` | 按位或 |
| 11 | && | 逻辑与 | 左到右 |
| 12 | ` | ` | |
| 13 | ?: | 三目运算 | 右到左 |
| 14 | = += -= *= /= %= <<= >>= &= ^= ` | =` | 赋值及复合赋值 |
| 15 | , | 逗号表达式 | 左到右 |
还有一些有问题的表达式

这个也是,不能确定操作数什么时候准备好

1575

被折叠的 条评论
为什么被折叠?



