1.二进制
😊其实二进制也好,十进制也好,都是数值的不同表示方法,正如十进制每一位表示100、101、……
二进制也是如此,20、21、……
更有意思的是2进制转8进制、二进制转16进制
8进制第一位和2进制前三位等价
同理
2.原码、反码、补码
我们都知道整型4个字节、32个比特
因此,原码对应数值的32位二进制序列
这个序列第一位代表正负、0正、1负
其它31位为数值位
如1代表00000000000000000000000000000001
注意:正数的原码、反码、补码都相同
而负数的反码,符号位不动,数值位取反
补码在反码基础上+1
如-1原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码:111111111111111111111111111111111111
3.移位操作符
3.1左移位操作符<<
上原理图😊😊😊😊😊😊
3.2右移位操作符>>
分两种情况:逻辑右移、算术右移
取决于编译器
4.位操作符:&、|、^、~
4.1&
按位与:遇0则0,双1则1
4.2|
按位或:遇1则1,双0则0
4.3^
按位异或:相同为0,相异为1
4.4~
按位取反:符号位不变,数值位全部取反
5.逗号表达式
express1,express2,express3,……
- 按照顺序从左到右执行
- 返回最后一个表达式的值
//看结果
int a = 1;
int b = 8;
int c = (a > b, a = b + 12, b, a = b + 1);
printf("%d", c);
6.下标访问[]、函数调用()
[]用于数组索引;
()用于函数传参,函数名是第一个操作数,剩余操作数为传给函数的参数
7.操作符的优先级、结合性
这两个重要属性决定表达式求值顺序
7.1优先级
例如*、/优先于+、-
7.2结合性
优先级相同,大部分运算符是左结合(从左到右),少部分是右结合(从右到左),例如赋值运算符(=)
下面给出部分操作符优先级
- 圆括号(())
- 自增运算符、自减运算符(++)、(–)
- 单目运算符(+)、(-)
- 乘除:*、/
- 加减:+、-
- 关系操作符:<、>
- 赋值操作符:=
具体参考:https://zh.cppreference.com/w/c/language/operator_precedence
8.整型提升
C语言整型算数运算总是以默认整型的精度来进行的
为了获得这个精度,字符型、短整型都必须提升精度,来适应CPU计算😊😊😊😊😊
因为表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsignedint,然后才能送⼊CPU去执⾏运算
9.算数转换
不同类型的数值进行运算,要进行类型转换
排位如下:
- long double
- double
- float
- unsigned long int
- long int
- unsigned int
- init
10.表达式求值
抽象表达式
10.1
a*b+c*d+e*f;
比 +优先级高,但 ab与c*d那个先计算呢?
假如a,b,c,d内部表达式有相互影响的量,很有可能结果不如我们所料
10.2
c+--c;
虽然–优先级高于+,但第一个c是想用原来的还是减后的?
即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的计算路径,那这个表达式就是存在潜在⻛险的,建议不要写出特别负责的表达式。