本人能力有限,难免有叙述错误或者不详细之处!希望读者在阅读时可以反馈一下错误以及不够好的地方!感激不尽!
目录
在C语言中,操作符的功能以及使用还是非常重要的,以下则是部分操作符的详解。
算数操作符
+ - % / *
分别为加法,减法,取余数,除法,乘法。
一些细节:
1.以上几个操作符中,除了%以外,其他的操作符的操作数都可以是整数或者浮点数,只有%的操作数只能是整数。% 操作符的两个操作数必须为整数。返回的是整除之后的余数。
2. 3/2等于多少?不是想当然的1.5,而是1,当/左右两边的操作数为整数时执行整数除法,而是浮点数的时候才执行浮点数除法,也就是3/2.0才能得到1.5.
位移操作符
<< 左移操作符
>> 右移操作符
位移操作符移动的是二进制位,只能作用于整数。
在介绍这有关于位运算的操作符之前应该先了解内存中整数的存储方式:
1.正整数
对于正整数来讲,它的原码,补码,反码都是一样的。
只要放到内存里的数值都是以补码形式存放的。
原码是什么?
在内存中存储数据时的使用的是二进制,以整型为例,int num = 10时,10在内存中的存储情况时这样的:
int 10 = 00000000000000000000000000001010//二进制表示
转换为十进制即从右向左一次为0*2的0次方,1*2的1次方,0*2的2次方,1*2的3次方,0*2的4次方……依次相加后得到的结果。
因为10是一个正整数所以对于10来说它的原码反码补码如下:
原码:00000000000000000000000000001010
反码:00000000000000000000000000001010
补码:00000000000000000000000000001010
2.负整数
而对于负整数来说,它的正反补码则不一样。
首先,为了表示一个负数,符号位是最高的一位,则-10的原码如下:
int -10 = 10000000000000000000000000001010//二进制表示
负数的反码就是在其原码的基础上 符号位不变 其他位取反。
int -10 = 10000000000000000000000000001010//原码
int -10 = 11111111111111111111111111110101//反码
负数的补码就是在其反码的基础上+1
-10 = 11111111 11111111 11111111 11111011//-10的补码,也就是内存中真正存放-10的状态
左移操作符:
而左移操作符的效果如下,以10为例:
左边抛弃,右边补0。
00000000000000000000000000001010
num<<1
00000000000000000000000000010100
右移操作符:
右移操作符右以下两种位移:
1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃,左边补原符号位,正的补0,负的补1 vs2019选择的是算数右移
其作用原理规则如上,效果不做演示。
注意!对于移位运算符,不要移动负数位,这个是标准未定义的!
int =10;
num>>-1;//error
位操作符
& //按位与
| //按位或
^ //按位异或
注意!他们的操作数必须是整数。
& - 按二进制位与: 1& 1 = 1 ;1& 0=0;0&0 = 0
| - 按二进制位或: 1|1=1 ; 1|0=1 ;0|0 =0
^ - 按二进制位异或:相同为0,相互异为1,
这些位移操作符看上去好像没什么用,但是如果我们碰到了如下的一道题呢?
不创建临时变量交换两个数:
这个时候我们就可以巧妙的运用以下位移操作符了
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d b=%d\n", a, b);
a = a ^ b; //a=3^5
b = a ^ b; //3^5^5 --> b=3
a = a ^ b; //3^5^3 --> a=5
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
赋值操作符
=
也就是我们平常经常接触的操作符=了。
其更多的功能不做过多细述,不过当连续赋值时其顺序依然要记录一下:
a = x = y-1;
同等效于:
x = y-1;
a = x ;
但是连续赋值的写法非常不讨喜,难以阅读和更改维护,所以请尽量避免。
复合赋值操作符:
%=
*=
/=
-=
+=
>>=
<<=
&=
^=
|=
其主要作用主要是为了简洁代码,其效果举例如下:
int x = 0;
int y = 10;
x = x + y;
等效于:x += y;
注意!赋值运算符本身的优先级是比较低的,当然复合运算符也是一样的!
以下的陷阱代码还请留心:
int x = 1;
int y = 2;
x *= y + x;
由于+的优先级高于复合赋值运算符,x其实会等于:x = 1*(1+2)
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
其中大部分的操作符作用我已在前面的文章有过记述:C语言的部分操作符概述与static概述_lanload的博客-优快云博客
关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
不做过多细述。
逻辑操作符
&& 逻辑与
|| 逻辑或
一些细节:
&&左边为假,右边就不计算了
||左边为真,右边就不计算了
条件操作符
exp1?exp2:exp3
int r = a>b?a:b
A是否大于B?如果大于取A,小于取B
其本质是,?前的表达式为真,取表达式2;?前表达式为假,取表达式3。
逗号表达式
exp1,exp2,exp3,.......expn
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果
请注意!执行是执行,取结果是取结果,请不要弄混了两者的概念而只是去执行最后一个逗号表达式!
[ ] 下标引用操作符,->操作符
[ ]
通常应用于数组
arr[7] == 7[arr]
arr[7] ---->*(arr+7),arr是首元素的地址,其实是等价的
->
左边是结构体指针,箭头指向结构体内的成员,使用箭头的时候不需要解应用符号*
ps->age等价于(*ps).age
隐式类型转换
我们知道,Char类型的数据存储只有8个比特位,但是C语言中进行整型算术运算总是至少以缺省整型类型的精度来进行,char类型的本质依然是整型,所以在一些情况下,会涉及到隐式类型转换也就是整型提升的情况。
隐式类型转换是发生在程序运行时的转换,平常情况下我们时无法观察到的,这种转换的发生情况一般是计算char类型或者短整型相加时
char a,b,c;
a = b + c;
整型提升,简单来讲,CPU进行整型运算字符型和短整型时,因为CPU内部的运算硬件和寄存器的长度要求都是int长度的,所以,CPU计算字符型时,会把char类型整型提升膨胀到32位进行计算。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度才能计算。
整型提升到底是怎样的?
进行整型提升时,补充至32位的数值是由符号位决定的,比如-1的整型提升如下
负数的整型提升:
char a = -1;
char类型内存存储-1时的情况:
11111111(补码)
11111111111111111111111111111111(整型提升后)
由符号位补充至32位.
正数的整型提升:
char a = 1;
char类型内存存储1时的情况:
00000001(补码)
00000000000000000000000000000001(整型提升后)
由符号位补充至32位.
当无符号类型整型提升时,补充至32位的是0
整型提升的例子:
char a = 0xb6;
short b = 0x11b6;
int c = 0xb6000000 ;
if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
以上的输出结果是:c
其中,ab发生了整型提升,因为其变量类型已经无法容下其能容纳的数值大小,在发生了整型提升后其值变成了负数,不能满足if的条件。
为什么会变成负数?以a为例
有符号的char类型的取值范围是-128~127,a转换为2进制后的值远远超出了这个范围,这个时候触发了整型提升,我们先写出来a的二进制序列:1011 0110
a是一个char类型的数据,我们可以看到这个时候数据其实因为超出了范围将符号位变成了1,整个数值变成了负数。
至于更多的细节,如转换成十进制后的数值,我们将在之后的数据存储中讨论它。
第二个例子:
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字
节.
表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节
算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
发生于数据类型不同时。