1.操作符分类
- 算术操作符:+ 、- 、 * 、 / 、%
- 移位操作符:<< >> //移动的是二进制位
- 位操作符:& | ^ //使用二进制位进行计算
- 赋值操作符:= += -= *= /= %= <<= >>= &= |= ^=
- 单目操作符:! ++ -- & * + - ~ sizeof
- 关系操作符:> >= < <= == !=
- 逻辑操作符:&& ||
- 条件操作符:? :
- 逗号表达式:,
- 下标引用:[]
- 函数调用:()
- 结构成员访问:. 、 ->
上述的操作符,在之前的博客中已经介绍过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符。今天继续介绍一部分,操作符中有一些操作符和二进制有关系,首先先铺垫一下二进制和进制转换的知识。
2.二进制和进制转换
其实我们经常能听到2进制、8进制、10进制、16进制 这样的讲法。
其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。
比如:数值15的各种进制的表示形式:
15的2进制:1111
15的8进制:17
15的10进制:15
15的16进制:F
//16进制的数值之前写:0x 8进制的数值之前写:0
2.1 2进制转10进制
2.1.1 10进制转2进制
2.2 2进制转8进制和16进制
2.2.1 2进制转8进制
8进制的数字每一位是0~7,0~7的数字,各自写成2进制,最多有3个2进制位就足够了,比如7的二进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个二进制位会换算一个8进制位,剩余不够3个2进制位的直接换算。
比如:2进制的0110 1011,换成8进制:0153,0开头的数字,会被当做8进制。
2.2.2 2进制转16进制
16进制的数字每一位是0~9,a~f 的,0~9,a~f 的数字,各自写成2进制,最多有4个2进制位就猪足够了,比如f的二进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进制位会换算一个16进制位,剩余不够4个二进制位的直接换算。
比如:2进制的0110 1011,换成16进制:0x6b,16进制表示的时候前面加0x。
3.原码、反码、补码
整数的2进制表示方法有三种,即:原码、反码和补码。
有符号整数的三种表示方式均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。(无符号数,所有位都是数值位)
符号位都是用0表示“正”,用1表示“负”。
正整数的的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码加一
(学会逆向思维。注:补码—>原码的另一个方法:补码取反再加一)
对于整数来说:数据存放内存中其实存放的是补码。
为什么?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器),此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
4.移位操作符
//移动的是二进制位(对补码进行操作)
<< 左移操作符 //二进制整体左移,右边补0
>> 右移操作符 //逻辑右移:左边补0,。 算术右移:左边补符号位。(取决于编译器选择哪一种。但通常是算术右移)
注:移位操作符的操作数只能是整数,不要移动负数位,这个是标准未定义的。
(左移一位有乘2的效果,右移一位有除2的效果)
5.位操作符
注:a ^ a = 0 0 ^ a = a
例题:不用第三个局部变量,交换两个整型的值。
主要算法过程:a = a ^ b ;
b = a ^ b ;
a = a ^ b ;
例题:一个数的二进制表示中1的个数。
解法一:
解法二:
解法三:
例题:判断一个数是不是2的次方。
提示:if(n&(n-1)==0)
{
printf(“yes”);
}
例题:将13的二进制序列的第5位修改为1,然后再改回0。
6.逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
举个例子:
7.下标访问[],函数调用()
7.1 []下标引用操作符
操作数:一个数组名 + 一个索引值(下标)
7.2 函数调用操作符
8.结构成员访问操作符
8.1结构体
C语言已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。
描述一个学生需要名字、年龄、学号、身高、体重等;
描述一本书需要作者、出版社、定价等。C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。
8.1.1 结构的声明
8.1.2 结构体变量的定义和初始化
8.2 结构体成员访问操作符
8.2.1 结构体成员的直接访问
8.2.2 结构体成员的间接访问
后续文章会说明
9. 操作符的属性:优先级、结合性
C语言的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
9.1 优先级
优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。
例题:3 + 4 * 5 结果是:23
优先级表格参考:https://zh.cppreference.com/w/c/language/operator_precedence
9.2 结合性
如果两个操作数的优先级相同,那么就看结合性。
例:5 * 6 / 2 ;
“ * ”和“ / ”的优先级相同,但他们丢是左结合,所以从左向右执行。
10.表达式求值
10.1 整型提升
C语言中整型算术运算总是至少以却省(默认)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
如何进行整型提升呢?
1.有符号整数提升是按照变量的数据类型的符号位来提升的
2.无符号整数提升,高位补0。
10.2 算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
1.long double
2.double
3.float
4.unsigned long int+
5.long int
6.unsigned int
7.int
从下到上转换。
作者自述:本文主要针对C语言的一些初级概念,对此作一个简单介绍。本文制作不易,求求动动你们发财的小手点个赞和关注,这是对我创造最大的动力。后续我也会跟进内容,尽量一周至少一次,保证内容的质量。如果有想知道的内容或者有建议的地方,欢迎后台私信或者在本文留言哦。感谢各位的支持捏Thanks♪(・ω・)ノ。