操作符的种类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用与函数调用
- 结构体成员访问
算术操作符
作为最常见的操作符,算术操作符包括加、减、乘、除、取余共计四个运算符分别表示符号为:
+ - * / %
算术操作符比较简单,但是也有一些值得我们注意的:
/ 操作符对于不同类型的操作数,会产生不同的运算结果:
- 当操作数为两个整数时,计算方式为整除
- 当操作数为一个或者两个浮点数时,计算方式为浮点
此外%操作符的两个操作数必须是整数。
移位操作符
移位操作符作分为两种,由成双的大于号或者小于号组成,一个是左移操作符 << ,另一个是右移操作符 >>,我们可以通过箭头的指向来辨识哪一种操作符,箭头指向左边的为左移操作符,箭头指向右边的为右移操作符。要注意的是,移位操作符的使用只能适用于整数,由于计算机能够处理的信息是二进制,所以为了弄清楚移位操作符的使用,我们首先需要了解整数在内存中的存储。
整数在内存中的二进制中的存储
我们都知道,一个整数类型的数共占用4个字节,1字节=8位(1 byte = 8bit),整数在内存中的存储是用32bit来存储的,每个bit位上是一个二进制的数,其中最高位放置的数被称作符号位,符号位起到表示数的正负,其中1表示负数,0表示为正数。
如15和-11的二进制表示形式分别表示为:
整数的3种二进制形式:原码、反码,补码
了解了整数的二进制表示形式后,我们还需要知道关于正数的二进制形式其实是有三种的,其中我们能正常写出来的,如上图的样式,称之为原码,还有一种叫做反码,反码的形式是在原码的基础上做出改变,原码的符号位不变,其余二进制序列按位取反,那么什么是按位取反呢?我们知道整数的二进制表示中,只有两个数,一个是0,一个是1,所以就能够很清楚的知道,按位取反其实就是将二进制序列中的1改为0,将0改为1。
特别需要我们注意的是,正整数的二进制原码、反码、补码三个序列是相同的,我们只需要写出原码的序列即可,而负整数的原码、反码和补码的三个序列是不同的,需要先写出原码的序列,然后在原码的基础上依次写出反码和补码的序列。
如15和-11的反码二进制序列分别表示为:
在知道了整数反码二进制表示形式后,要得到最后的补码就只需要在反码的基础上加上1就完成了 ,即补码=反码+1,而补码的二进制序列正是我们在使用移位操作符时所需到的。补码作为整数在内存中的存储序列,在使用移位操作符时也是通过反码来计算的。
如15和-11的补码二进制序列分别表示为:
左移操作符
在了解了上面的整数二进制序列相关知识后,我们就能很好学习左移操作符的相关知识,左移操作符的操作对象是整数的补码二进制形式,通过将操作数的二进制序列左移n位,得到新的值。这个过程可以看作将操作数补码的二进制序列左侧去掉n位数字,然后在右侧补上相同位数的0。
例如:15<<1 后得到结果30;-11<<1 后得到结果-22
正整数的左移后得到的结果,可以直接通过补码读取出来,而负整数的左移结果需要转换为原码我们才可以顺利读取得到的结果。这个过程也可以看作是对操作数乘上了2,将数增大为原来的2倍。
右移操作符
右移操作符又分为两种方式,一个是逻辑右移,另一个是算数右移。而在编译器中,一般运行的是算数右移。
逻辑右移
与左移操作符相似,逻辑右移,作对象同样是整数的补码二进制形式,通过将操作数的二进制序列右移n位,得到新的值。这个过程可以看作将操作数补码的二进制序列右侧去掉n位数字,然后在左侧补上相同位数的0。
例如:
理论上应该会得到上面的结果,但是编译器一般都为算数右移,所以这里就不放编译器运行结果的图了。
算数右移
与逻辑右移有所不同的是,算数右是将操作数补码的二进制序列右侧去掉n位数字,然后在左侧补上想同位个与该整数符号位相同的数。
。
位操作符
位操作符分共有三个,并且操作的同样是二进制数的补码形式,操作数为两个整数。分别为一下3种
- 按位与:&
- 按位或 :|
- 按位异或 :^
按位与
虽然长得与取地址操作 & 一样,但是两者的作用完全不一样,符按位与通过比较两个整数的补码二进制形式的对应相同位,如果两整数对应二进制位的数都为1则该位取1,否则取0。
例如:15 & -11 = 5
按位或
按位或通过比较两个整数的补码二进制形式的对应相同位,如果两整数对应二进制位的数有1则该位取1,全为0才取0。
例如:15 | -11 = -1
按位异或
按位异或通过比较两个整数的补码二进制形式的对应相同位,如果两整数对应二进制位的数相同则改位取0,相异则取1。
例如:15 ^ -11 = -6
赋值操作符
赋值操作符共有以下几种:
- =
- +=
- -=
- *=
- /=
- %=
- <<=
- >>=
- &=
- |=
- ^=
其中除了赋值操作符=其余均为复合赋值操作符,了解了上面的几种操作符,这里的复合赋值操作符自然也就信手拈来了,复合赋值操作符是一种缩写形式的操作符。例如:a = a + 10 可以缩写成 a += 10 , a = a / 10 可以缩写成 a /= 10等,此外也有一些我们需要注意的地方,如连续赋值需要注意赋值的先后顺序,看这样一段代码:
输出a和x的值是多少呢?答案是a和x的值都为20,这里的赋值先后顺序是从右往左边的,先将b+15的值赋值给x,然后再将x的值赋值给a,所以上面的代码又可以换成以下。
关于连续赋值还有一点需要注意的是,除最后一个等号右边可以写成表达式,其余的只能是单个变量,例如:a=x+1=b+10 这一写法是错误的,表达式x+1不能写在多个=之间,连续赋值写法虽然比较方便,但是可读性却不高,所以我们在写代码时非必要最好不要用连续赋值的写法。
单目操作符
单目操作符的操作数均为一个,分为以下几种:
- 逻辑反操作:!
- 正值:+
- 负值:-
- 取地址:&
- 计算操作数类型长度(单位为字节):sizeof
- 对一个数的二进制按位取反:~
- 自增符:++
- 自减符:--
- 强制类型转换:(类型)
- 解引用操作符:*
逻辑反操作
逻辑反操作! 顾名思义是将逻辑反过来,例如真变假,假变真。多用在条件语句的判断。
C语言中,用0表示假,用非0表示真,对用非0的数使用逻辑反操作后得到结果为0,而对于0使用逻辑反操作将的到默认结果1。
正负值
用于指定数的正负,这个比较简单,没有什么知识点,我们只需要知道,带有 + 号的数即为正数,带有 - 号的数为负数即可,此外正数的 + 号可以省略,但是负数的 - 号不可以省略。
取地址操作符
取地址操作符的作用是找到操作数的地址,我们常用的库函数scanf( )就需要与取地址操作符一同使用,通过取地址操作符找到操作数的地址,从而为操作数赋值,也可以单纯的取得操作数的地址打印出来,亦或是通过&操作符传递变量的地址给函数,使函数实参与形参建立真正意义上的联系。
sizeof操作符
sizeof操作符是用于计算的是操作数类型的长度,其单位为字节,其使用一般为sizeof 加上操作数,如sizeof 3、sizeof(3.14)、sizeof("abcd")等,其中括号的书写可省去,但在计算类型时不可省去,如sizeof (int) 、sizeof(char)。
看以下几个例子:
变量a的类型为int类型,占4个字节所以sizeof的计算结果为4;变量ch的类型为char类型,占一个字节,所以sizeof的计算结果为1。
按位取反操作符
按位取反操作符是将一个数的二进制补码各个位的数按位取反,简单来说,按位取反的意思其实就是将二进制序列中的1改为0,将0改为1,这里的取反也包括序列的符号位,也就是取反后,数的正负号将发生变化,此外,按位取反操作符的操作数为一个整形数。
例如:~11 = -12
自增运算符、自减运算符
自增与自减运算符,两者十分相似,所以这里放在一起将,这两个运算符又分为:前置和后置两种情况,下面将分别讲解前置和后置的使用。
前置使用
当自增和自减运算符前置使用时,会让操作数在进行该语句执行前将自身的值先上加1,然后再进行相关语句执行。
例如:
在将a的值赋给x前,a自身先自增1变为5后再赋值给x;在将b的值赋给y前,b自身先自减1变为4再 赋值给y。
后置使用
当自增和自减运算符后置使用时,会先让操作数先执行完当前语句,然后再将自身的值先减去1。
例如:
在将a的值赋给x后,a自身自减增为5;在将b的值赋给y后,b自身自减1变为4。
强制类型转换
强制类型转换可以将某一类型变量的值转变为另一类型,使用方法为(要转换成的类型) + 要转换的变量。
例如:
double类型的a被强制类型转换为int类型后的值为3,要注意的是强制类型转化为int型后保留的整数部分,并不会进行四舍五入,而是直接将当前整数部分保留下来。int型b强制类型转换为double型时默认后面带有小数点后6位数,所以转换后b的值为3.000000。
解引用操作符
解引用操作符又叫做间接访问操作符,作为一个指针中使用的操作符,其作用是通过操作数的地址来访问当前地址的变量。从而达到使用或更改该地址变量的需求。
例如:指针变量p存储a的地址后,可将通过指针将a中的值改变。
关系操作符
关系操作符分为以下几种:
- 大于:>
- 大于等于:>=
- 小于:<
- 小于等于:<=
- 等于:==
- 不等于:!=
关系操作符比较简单,相信小伙伴们应该都没有问题,就不多讲解了,唯一值得注意的是要分清楚单个=为赋值操作符,两个=为判断两数是否相等。
逻辑操作符
逻辑操作符分为以下两种:
- 逻辑与:&&
- 逻辑或:||
逻辑操作符总体来说也比较简单,用于逻辑判断,通常用于选择、循环语句中,但也有一些需要注意的地方。
逻辑与
与按位与有些相似,都是由&构成,但逻辑与是由两个&构成,并且操作数为两个。逻辑与的两个条件都满足时才为真,否则为假,需要注意的是,逻辑与是从左往右判断的,如果左边的条件不满足的话,那么右边的语句条件也就不用执行判断了。
例如:a的初始值为0,左边条件为假,后续语句就不再执行判断,只有a自身加1。所以最终结果为:a=1,b=2,c=3
逻辑或
与按位或有些相似,都是由 | 构成,但逻辑与是由两个 | 构成,并且操作数为两个。逻辑或的两个条件至少有一个满足即为真,两个都不满足才为假,需要注意的是,逻辑或同样是从左往右判断的,如果左边的条件满足的话,那么右边的语句条件也就不用执行判断了。
例如:a初始值为0条件为假,不满足,判断完a变为1,往右继续执行判断,b初始值为2,判断前先自增1变为3,条件为真,满足,后续不再执行判断。所以最终结果为:a=1,b=3,c=3
条件操作符
条件操作符也叫三目操作符,顾名思义,操作数为三个,表达式为:
exp1 ? exp2 : exp3
其中表达式exp1如果为真,则得到结果为exp2,否则结果为exp3。
例如:
逗号表达式
逗号表达式是一个由逗号隔开的多个表达式。这些表达式依次从左往右开始执行,最终结果为最后一个表达式。
例如:表达式依次从左往右执行,先执行表达式a++,a变为2;再执行表达式 a = b+10,a变为12;再执行表达式a,a不发生变化;最后执行表达式 b = a+1,b的值变为13,并且将最后一个表达式的结果13赋值给c,于是就得到下列结果。
下标引用操作符
下标引用操作符[ ]用于数组元素的访问,且[ ]的里面只能为常量表达式,从数组第一个元素开始至数组最后一个元素,下标大小从0开始依次加1,也就是说,一个存储n个元素的数组的下标范围是从0开始,以n-1结束的。
例如:通过下标引用操作符打印数组各个元素
函数调用操作符
函数调用操作符()用于接收一个或者多个操作数,一个固定操作数为函数名,位于()左边,剩余操作数位于()之间,用于传递函数的参数。
例如下面一个简单的函数调用:
#include<stdio.h>
int add(int x, int y)
{
return x + y;//返回x和y的和
}
int main()
{
int a = 2;
int b = 3;
int sum = add(a, b);
printf("max=%d", sum);
}
结构体成员访问
结构体成员的访问包含两个操作符,一个为 . 操作符,另一个为 -> 操作符,两者的应用场景不太一样。
点操作符
点操作符在结构体类型访问时均可使用,使用方法为在 . 操作符前加上要访问的结构体变量名,后面加上结构体成员名,即可正常访问结构体成员。
例如:s.age、s.name
#include<stdio.h>
struct stu
{
char name[10];
int age;
};
int main()
{
struct stu s = { "zhangsan",18 };
printf("name=%s age=%d\n", s.name, s.age);//name=zhangsan age=18
s.age = 17;//通过点操作符将age改为17
printf("name=%s age=%d\n", s.name, s.age);//name=zhangsan age=17
return 0;
}
箭头操作符
与点操作符不同的是,箭头操作符->只能够用于指针传参时使用来访问结构体成员,通传递的地址使用箭头操作符找到该地址处的变量,从而访问。使用方法为,在箭头操作符的前面加上要访问的结构体变量,即可正常访问结构体成员。
例如:s->name、s->age
#include<stdio.h>
struct stu
{
char name[10];
int age;
};
void set_age(struct stu* s)
{
s->age = 17;//通过箭头操作符将s中的age改为17
printf("name=%s age=%d\n", s->name, s->age);//name=zhangsan age=17
}
int main()
{
struct stu s = { "zhangsan",18 };
printf("name=%s age=%d\n", s.name, s.age);//name=zhangsan age=18
set_age(&s);
return 0;
}
结语
本期操作符的相关知识到这就介绍完了,如果感觉对你有帮助的话还请点个赞支持一下!有不对或者需要改正的地方还请指正,感谢各位的观看。