当我们学习C语言时会遇到许多操作符,在许多语句中都能使用,可以起到举足轻重的作用。不要小看这些操作码,下面让我详细讲解一下操作符。
操作符有很多:
算数操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号操作符 ......
算数操作符
+ - * / %
这些操作符都是我们经常见到的,如同数学运算符一样,适合于运算。但是算数运算符有一些注意事项:
1.除了%操作符以外,其他的几个操作符可以作用于整数和浮点数。
2.对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行就是浮点数除法。
3.%操作符的两个操作数必须为整数。返回的是整数之后的余数。
4.算数操作符一定在数学知识的基础上进行的。
移位操作符
<<(左移) >>(右移) 注:移位操作符的操作数只能是整数
移位操作符是基于二进制数的基础上操作的,移动的是二进制的数据。计算机存储的就是二进制的数据由0和1组成的。如:10: 0000 0000 0000 0000 0000 0000 0000 1010,而计算机存储的一般是一个数的补码。正数的原码反码补码都是一样的,而负数的原码得到后将除过符号位(最高位)不变剩下的位数一律取反得到,补码就是反码加1。
移位移动的是补码的二进制序列
例子:-10
原码:1000 0000 0000 0000 0000 0000 0000 1010
反码:1111 1111 1111 1111 1111 1111 1111 0101
补码:1111 1111 1111 1111 1111 1111 1111 0110
<<(左移)
就是把一个二进制数整体向左移动, 舍弃超出的部分,在移动的空白处加0。
比如:00000000000000000000000000000011向左移动一位就是:
00000000000000000000000000000110
>>(右移)
把一个二进制整数向右移动,舍弃超出的部分,但在空白的最高位应该加什么呢?
这里有两种情况:
1.算数右移:右边丢弃,左边补原来的符号位
2.逻辑右移:右边丢弃,左边补0
C语言没有明确规定是算数右移还是逻辑右移,一般编译器上采用的算数右移
我们可以举一个代码来证明:
#include<stdio.h> int main(void) { int a = -15; int b = a >> 1; printf("%d\n", b); return 0; }
a = -15(1000 0000 0000 0000 0000 0000 0000 1111) 原码
1111 1111 1111 1111 1111 1111 1111 0000反码
1111 1111 1111 1111 1111 1111 1111 0001补码
当15向右移一位
算数右移:1111 1111 1111 1111 1111 1111 1111 1000(-8)
逻辑右移:0111 1111 1111 1111 1111 1111 1111 1000 (8)
运行程序:
所以右移采用的是算数右移,左移不牵扯这样的事。
不要采用稀奇古怪的左右移方式:a << -1; b >> -3; 这样的方式虽然编译器不会报错,但是是一种标准为定义的行为。
位操作符
& //按位与
| //按位或
^ //按位异或
他们的操作数是整数
让我们来认识一下他们怎么用:
int main(void) { int a = 3;//补码:00000000000000000000000000000011 int b = 5;//补码:00000000000000000000000000000101 int c = a & b; &--对应二进制位有0则0,同时为1则为1 printf("%d\n", c); c = a | b; |--对应二进制有1则1,同时为0则为0 printf("%d\n", c); c = a ^ b; ^--对应二进制位两个相同则为0,两个不同则为1 printf("%d\n", c); return 0; }
赋值操作符(=)
赋值操作符可以将一个之前不满意的值进行替换,也就是重新赋值。
int high = 175;//不满意现在的身高
high = 188;//重新赋值
赋值操作符也可以连续使用,比如:
int a =10;
int b = 0;
int y = 20;
a = b = c + 10;但是可读性很差
复合赋值符号
+=、-=、*=、/=、 %=、 >>=、<<=、&=、|=、^=
比如x = x + 10:可以写做 x += 10;
单目操作符(只有一个操作数)
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
!:一般把逻辑判断的答案取相反的,比如在if while语句中的判断条件。
int main(void) { int flag = 0; if (!flag) printf("haha\n"); return 0; }
flag = 0; if(flag)相当于if(0)条件为假,但加上!就相当于条件为真,打印haha。
&和*应用于指针,&为取地址操作符。*为解引用操作符,引用指针指向变量,引用其实就是引用该变量的地址,解就是把该地址对应的东西打开。
int main(void) { int a = 10; int* p = &a; printf("%d\n", *p); return 0; }
sizeof 不是函数,是操作符,计算的是类型创建变量的大小,单位是字节
int main(void) { int a = 10; printf("%d\n", sizeof(int)); printf("%d\n", sizeof(a)); printf("%d\n", sizeof a); return 0; }
~:按二进制补码进行取反
int main(void) { int a = 0; printf("%d\n", ~a); //00000000000000000000000000000000 0的补码 //11111111111111111111111111111111 按位取反 //11111111111111111111111111111110 反码 //10000000000000000000000000000001 -1 return 0; }
前置、后置++:前置++就是先给变量加1在使用此值,后置++就是先使用此值在给变量加1
前置、后置--:前置--就是先给变量减1在使用此值,后置--就是先使用此值在给变量减1
int main(void) { int a = 1; int b = a; printf("%d\n", a++); printf("%d\n", ++b); printf("%d\n", a); return 0; }
第一个打印先打印a的值在给a+1;a就成了2,第二个打印是先b+1再打印,b最开始等于1,所以b+1 = 2,应打印2。
(类型)强制类型转换:把一个类型数据强制转换成另一个类型的数据,多用于将浮点数转换为整数。
nt main(void) { int a = (int)3.14; printf("%d\n", a); return 0;
3.14本来是个浮点数,不能将其放在整型变量中去,但将其强制类型转换就可以放入整型变量中去,但是会丢失原来的精度,将3.14打印出3。
关系操作符
用来做两种相同类型变量的比较的
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
逻辑操作符
&& 逻辑与 (并且)
|| 逻辑或 (或者)
举个通俗易懂的例子:
班长和副班长今天都必须来教室(&&)
班长或副班长今天有一个来教师就行(||)
int main(void) { int a = 0; int b = 0; if (a == 0 && b == 0) printf("haha\n"); if (a == 0 || b == 0) printf("hehe\n"); return 0; }
第一个if必须a 和b都为0才打印haha,而第二个if是a或者b只有一个为0就打印hehe。
||逻辑或操作符在执行时,当第一个条件已经满足时就不会对后面的条件进行判断。
&&逻辑与操作符在执行时,当第一个条件不满足时也就不会对后面的条件进行判断。
条件操作符
exp1 ? exp2 : exp3
条件操作符也被称为三目操作符,有三个操作数。
以表达式1为判断标准,如果表达式1为真,那就执行表达式2,相反如果表达式1为假,那就执行表达式3。
int main(void) { int a = 0; int b = 0; if (a > 5) b = 3; else b = -3; printf("%d", b); return 0; }
这个if可以用条件操作符代替
int main(void) { int b = 0; b = (b > 5) ? 3 : -3; printf("%d", b); return 0; }
逗号表达式
exp1, exp2, exp3, ...expn
逗号表达式就是用逗号隔开的多个表达式,从左到右依次执行。整个表达式的结果是最后一个表达式的结果。
举个例子:
int main(void) { int a = 1; int b = 2; int c = (a > b, a = a + 10, b = a + 1); printf("%d", c); return 0; }
这个代码中的c是多少呢?我们来分析一下,a>b是一个判断表达式,无实际赋值。a=a+10,证明a = 11。b = a + 1就相当于b = 12,然后将逗号表达式的最后一项赋值给c,所以c就是12。
下标引用、函数调用和结构成员操作符
1.[ ] 下标引用操作符
操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符
[ ] 的两个操作数就是arr和9
2()函数调用操作符
接受一个或多个操作数:第一个操作数是函数名,剩余的操作数是传给函数的参数。
int add(int x, int y) { return x + y; } int main(void) { int a = 0; int b = 0; scanf("%d %d", &a, &b); int c = add(a, b); printf("%d\n", c); return 0; }
这个简单的程序就使用了函数调用操作符add(a, b),有三个操作数add函数名、a、b。
3.结构成员操作符
结构成员操作符在结构体中会用到,分别为.和->操作符。
. 结构体.成员名
-> 结构体指针->成员名
struct Book { char name[30]; float p; }; int main(void) { struct Book b1 = { "C语言", 55.5f }; printf("%s %.1f\n", b1.name, b1. p); return 0; }
使用.可以访问结构体中的数据。
struct Book { char name[30]; float p; }; void print(struct Book* pp) { printf("%s %.1f\n", pp->name, pp->p); } int main(void) { struct Book b1 = { "C语言", 55.5f }; print(&b1); return 0; }
当用到指针指向结构体时,可以使用->访问结构体中的数据。