1.操作符的分类
算术操作符 : + - * / %
移位操作符 : << >>
位操作符 : & | ^
赋值操作符: = += -= *= /=
单目操作符 : ! sizeof + - ~ & *
关系操作符 : > < >= <= == !=
逻辑操作符 : && ||
条件操作符 : ? : :
逗号表达式 : ,
下标引用、函数调用和结构成员 : [ ] () . ->
下面我们将一一学习这些操作符
2.算数操作符
+ - * / %
这里的 “+” “ - ” “ * ” 是我们非常熟悉的,就不再介绍了,但“ / ” 和“ % ”值得我们注意
1.除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2.对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3.% 操作符的两个操作数必须为整数。返回的是整除之后的余数
整数的二进制表示
整数的二进制表示有三种形式:
原码
反码
补码
1.正整数原码,反码,补码相同
2.负数的原码,反码,补码需要计算
不管是正整数还是负整数都可以写出二进制原码
1.根据正负直接写出的二进制序列就是原码
对于正数15,它的原码,反码,补码相同,而负数则可通过下图的方式进行计算:
整数在内存中存储的是补码,计算的时候也是使用补码计算的
3.位移操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
这里左移和右移移动的是二进制
3.1左移操作符
我们来分析下面的代码
右移分为两种:
算数右移 :右边的丢弃,左边补原来的符号位
逻辑右移 :右边的丢弃,左边直接补0
一般编译器采用的是算数右移,C语言没有明确规定到底是算数唯一还是逻辑位移。
让我来运行上面的代码:
此时a的值还是15,但b得到的是a右移一位的结果7,而且a的值没有发生改变
判断VS编译器采用的是逻辑位移还是算数位移:
使用下面的代码来判断:
因为-15用两种位移产生的结果是不同的:
上述代码运行结果:
因此VS编译器采用的是算数位移。
而且我们发现右移一位对正数有 /2 (除2)的效果
3.2右移操作符
左移操作符就是左边丢弃,右边补0
同样用代码来学习:
警告⚠ :
对于移位运算符,不要移动负数位,这个是标准未定义的。
4.位操作符
位操作符有:
& 按位与
| 按位或
^ 按位异或
注:他们的操作数必须是整数
按位与 &
按位与也是操作二进制位,对应两个二进制的每一位,有0则为0,两个同时为1才是1.
分析下面代码运行的结果:
、
程序运行结果:
按位或 |
按位或计算方法:对应两个二进制的每一位,有1则为1,两个同时为0才是0
分析下面代码运行的结果:
程序运行结果:
按位异或 ^
按位异或计算方法:对应两个二进制的为一位,相同则为0,相异则为1
分析下面代码结果:
程序输出结果:
一道面试题:
不能创建临时变量(第三个变量),实现两个数的交换。
方法一:
方法二:
原理:
两种方法都能交换a,b,但方法一可能会溢出,而方法而不会
5.赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋 值。
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果
6.单目操作符
6.1 单目操作符介绍
~ 的用法:
~ 对一个数的补码二进制位取反,包括符号位
扩展:将二进制某一位由0改成1,1改成0的方法
6.2 sizeof 和 数组
sizeof是一个操作符,不是函数,sizeof计算的是类型创建变量的大小,单位是字节
sizeof用法:
sizeof计算数组大小:
分析下面的代码:
void test1(int arr[]) { printf("%d\n", sizeof(arr));//(2) } void test2(char ch[]) { printf("%d\n", sizeof(ch));//(4) } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//(1) printf("%d\n", sizeof(ch));//(3) test1(arr); test2(ch); return 0; } 问: (1)、(2)两个地方分别输出多少? (3)、(4)两个地方分别输出多少?
再(2),(4)中打印的是指针变量的大小,32位系统下4个字节,64位系统下8个字节
7.关系操作符
8.逻辑操作符
闰年的判断:
int main() { int y = 0; scanf("%d", &y); if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) { printf("是闰年\n"); } else { printf("不是闰年\n"); } return 0; }
笔试题
程序的输出结果是什么?
&&操作符,左边为假,右边就不计算了
程序的输出结果是什么?
||操作符,左边为真,右边就不计算了
区分逻辑与和按位与
区分逻辑或和按位或
9. 条件操作符
exp1 ? exp2 : exp3
条件操作符也称为三目操作符(有三个操作数)
条件操作符可以简化代码:
用条件操作符求两数的较大值:
10. 逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
分析:
答案是13
逗号表达式的某些妙用:
11. 下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
struct Book { char name[30]; char author[20]; float price; }; void Print(struct Book* p) { //printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price); printf("%s %s %.1f\n", p->name, p->author, p->price); } int main() { struct Book b1 = { "C语言","李华",30.5f }; struct Book b2 = { "数据结构","张三",50.5f }; printf("%s %s %.1f\n", b1.name, b1.author, b1.price); printf("%s %s %.1f\n", b2.name, b2.author, b2.price); Print(&b1); Print(&b2); return 0; }
3. 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
struct Book { char name[30]; char author[20]; float price; }; void Print(struct Book* p) { //printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price); printf("%s %s %.1f\n", p->name, p->author, p->price); } int main() { struct Book b1 = { "C语言","李华",30.5f }; struct Book b2 = { "数据结构","张三",50.5f }; printf("%s %s %.1f\n", b1.name, b1.author, b1.price); printf("%s %s %.1f\n", b2.name, b2.author, b2.price); return 0; }
到这里,C语言的操作符我们就基本学完了。