目录
一、操作符分类
算术操作符:+、- 、* 、/ 、%
移位操作符: <<、>>
位操作符:& 、|、 ^、 ~
赋值操作符:= 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
单⽬操作符:!、++、--、&、*、+、-、~ 、sizeof、(类型)
关系操作符:> 、>= 、< 、<= 、 == 、 !=
逻辑操作符:&& 、||
条件操作符:? :
逗号表达式: ,
下标引⽤:[ ]
函数调⽤: ( )
结构成员访问: . 、->
下面我们主要介绍二进制相关操作符、逗号表达式、下标引用、函数调用、结构成员访问
二、二进制
1.进制转换
10进制转2进制:模二除二,余数反读
对n循环%2、/2直到n为零,将得到的余数倒序输出
2进制转10进制:每位乘权重,求和
二进制从低位开始,每一位依次乘2的k次方(k=0,1,2...),对所有结果求和
注:第一位从2的零次方开始!
#include<stdio.h>
//十进制转二进制
void ten_two(int x)
{
int arr[32] = { 0 };
int i = 0;
//模2除2
while (0 != x)
{
arr[i++] = x % 2;
x /= 2;
}
//倒序输出
for (i = 31; i >= 0; i--)
{
printf("%d", arr[i]);
if (i % 4 == 0)
{
printf(" ");
}
}
printf("\n");
}
//二进制转十进制
void two_ten(int x)
{
int ret = 0;
int i = 0;
while (0 != x)
{
int pow = 1;
//计算权重
for (int j = 0; j < i; j++)
{
pow *= 2;
}
i++;
//求和
ret = ret + x % 10 * pow;
x /= 10;
}
printf("%d\n", ret);
}
int main()
{
ten_two(15);//输出:0000 0000 0000 1111
two_ten(1111);//输出:15
return 0;
}
2进制转8进制:3位转1位
000——0 001——1 010——2 011——3
100——4 101——5 110——6 111——7
2进制转16进制:4位转1位
0000——0 0001——1 0010——2 0011——3
0100——4 0101——5 0110——6 0111——7
1000——8 1001——9 1010——A(10) 1011——B(11)
1100——C(12) 1101——D(13) 1110——E(14) 1111——F(15)
2.原码、反码、补码
有符号整数分为包含符号位和数值位两部分。在二进制中,最高位为符号位(0表示正,1表示负),其余位为数值位
正数
原码、反码、补码均相同
负数
原码:直接将数值按照正负形式翻译为二进制
反码:符号位不变,其他位按位取反
补码:反码+1
注:补码转原码,原码转补码均可用“取反+1”
-13
原码:1000 0000 0000 1101
反码:1111 1111 1111 0010
补码:1111 1111 1111 0011
在计算机系统中,数据统一用补码来存储。
原因:1.CPU只有加法器,利用补码可以将加法和减法统一处理;2.原码和补码相互转换的运算过程相同,不需要额外的硬件电路
3.移位操作符
<<操作符
左边丢弃,右边补零(左移一位——>乘2)
>>操作符
1.逻辑右移:左边补零,右边丢弃
2.算数右移:左边补原来符号位,右边丢弃(右移一位——>除以2)
#include<stdio.h>
int main()
{
//左移操作符
int n = 10;
printf("%d\n", n);//输出:10
printf("%d\n", n << 1);//输出:20
return 0;
}
左移操作符演示
#include<stdio.h>
int main()
{
//右移操作符
int n = 10;
printf("%d\n", n);//输出:10
printf("%d\n", n >> 1);//输出:5
return 0;
}
逻辑右移演示
算术右移演示
注:不要用移位操作符移动负数位,这个是标准未定义的!
int n = 10;
n << -1;//错误的用法
4.位操作符
&(按位与)
有0则为0。将两个整形转换为二进制补码,若对应位置上,两数有一个为0,则结果的对应位置也为0
|(按位或)
有1则为1。将两个整形转换为二进制补码,若对应位置上,两数有一个为1,则结果的对应位置也为1
^(按位异或)
相同为0,相异为1。有0则为0。将两个整形转换为二进制补码,若对应位置上两数相同,则结果的对应位置为0,否则为1
~(按位取反)
包括符号位,全部取反(取反一次——>乘-1加-1)
注:所有按位运算结果均为补码,输出时应转换为原码
#include<stdio.h>
int main()
{
int a = -3;
int b = 5;
printf("%d\n", a & b);//输出:5
printf("%d\n", a | b);//输出:-3
printf("%d\n", a ^ b);//输出:-8
printf("%d\n", ~a);//输出:2
return 0;
}
一些二级结论
三、逗号表达式
1.用法
语句,语句,……,语句
返回值为最后一个表达式的结果
2.作用
有利于简化过程,防止代码冗余
//不用逗号表达式
a = GetVal();
CountVal(&a);
while (a > 0)
{
//业务处理
a = GetVal();
CountVal(&a);
}
//使用逗号表达式
while (a = GetVal(),CountVal(&a), a>0)
{
//业务处理
}
四、下标访问和函数调用
1.下标访问
操作数:一个数组名+一个索引值
arr[5]//操作数为arr和5
原则上数组名和索引值可以交换顺序,但为了方便理解,我们通常不这么写
5[arr]//不建议此种写法,等价于arr[5]
2.函数调用
操作数:函数名+实参
test();
test(a);
test(a,b,c……);
实参可以为0个,但必须有且仅有一个函数名
五、结构体访问
1.操作符 .
用法:结构体变量名.成员名
#include<stdio.h>
//结构体声明
typedef struct Point
{
int x;
int y;
}Point;
int main()
{
Point n = { 123,456 };
Point* pn = &n;
printf("%d %d\n", n.x, n.y);//输出:123 456
printf("%d %d\n", (*pn).x, (*pn).y);//输出:123 456
}
2.操作符 ->
用法:结构体指针->成员名
#include<stdio.h>
//结构体声明
typedef struct Point
{
int x;
int y;
}Point;
int main()
{
Point n = { 123,456 };
Point* pn = &n;
printf("%d %d\n", pn->x, pn->y);//输出:123 456
}
六、操作符属性
1.优先级
含义:一个表达式有多个操作符时,决定先进行哪个操作的规则
常用操作符优先级:
1 | () |
---|---|
2 | ++、-- |
3 | +、-(单目) |
4 | *、/(双目) |
5 | +、-(双目) |
6 | ==、>、<等关系操作符 |
7 | =、+=、-=等赋值操作符 |
2.结合性
含义:当在一个表达式中连续使用多个相同优先级的操作符时,决定这些操作符应该如何结合或分组的规则
大部分操作符都是左结合(从左到右执行),只有少部分操作符为右结合(从右到左执行),比如赋值操作符。
参考文档:C 运算符优先级 - cppreference.com
七、表达式求值
1.整形提升
整形提升的含义
字符(char)和短整型(short)操作数在使用前被转换为普通整形
整型提升的规则
按照变量数据类型的符号位补充,负数则全补1,正数则全补0
char a = -1;
//原型:
//1111 1111
//整形提升为:
//1111 1111 1111 1111
char a = 1;
//原型:
//0000 0001
//整形提升为:
//0000 0000 0000 0001
整型提升的意义
CPU的通用寄存器长度为int类型字节长度。CPU难以实现8比特字节位直接相加,所以表达式中各种长度可能小于int类型的整型值,都必须转换成int或unsigned int,才能送入CPU中运算
char a, b, c;
……
a + b;
//a和b先转换提升为int类型,然后将两个int相加,最后将结果截断为char类型
2.算术转换
当表达式中的各操作数类型不同时,需将各操作数转换为相同类型,否则操作无法进行
规则:低字节向高字节转换
八、问题表达式
1.问题表达式示例
示例一
a*b + c*d + e*f
路径一:先将乘法a*b、c*d、e*f全部算出来,最后将结果相加
路径二:先计算两个乘法a*b、c*d,结果相加,再计算e*f,所有结果相加
当前面的运算会影响后面的运算时,这种运算顺序未定的表达式,最终得到的结果是无法预料的!
示例二
int a = 3;
a + --a;
左操作数的获取在右操作数之前还是之后不定,结果可能为3 + 2,也可能为2 + 2
2.问题表达式解决
使用括号
使用括号将运算顺序不定的表达式括起来
((a*b) + (c*d)) + e*f
拆表达式
将复杂的表达式拆分成多个简单的表达式
int a = 3;
--a;
a + a;
最后,感谢大家观看!