1. 算术操作符
算术操作符分为:+、-、*、/、%
- 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
- 对于 / 操作符如果两个操作数都为整数,执行整数除法;而只要有浮点数,执行的就是浮点数除法。
- % 操作符的两个操作数必须为整数,返回的是整除之后的余数。
#include <stdio.h> int main() { //int r = 7 / 2; //printf("%d\n", r);//3 //double d = 7 / 2; //printf("%lf\n", d);//3.000000 double d = 7 / 2.0; printf("%lf\n", d);//3.500000 return 0; }
#include <stdio.h> int main() { int r = 15 % 8;//% 得到的是整除后的余数 printf("%d\n", r);//7 return 0; }
以下写法是错误的:
#include <stdio.h> int main() { int n = 0; int r = 5 / n; return 0; }
2. 移位操作符
<< 左移操作符
>> 右移操作符
注: 移位操作符的操作数只能是整数。
int main() { int a = 15; int b = a >> 1;//移动的是a中的2进制信息 return 0; }
我们先来看一下整数的三种二进制表示形式:
int main() { int a = 15; //00000000000000000000000000001111 - 原码 //00000000000000000000000000001111 - 反码 //00000000000000000000000000001111 - 补码 int b = -15; //10000000000000000000000000001111 - 原码 //11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码) //11111111111111111111111111110001 - 补码(反码+1就是补码) //整数在内存中存储的是补码 //计算的时候也是使用补码计算的 return 0; }
注: 符号位是1表示负数,符号位是0表示正数
2.1 右移操作符
右移运算分两种:
- 算术右移:右边丢弃,左边补原来的符号位
- 逻辑右移:右边丢弃,左边直接补0
注: C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移
//移位移动的是补码的二进制序列 #include <stdio.h> int main() { int a = 15; int b = a >> 1; printf("%d\n", b);//7 printf("%d\n", a);//15 return 0; }
#include <stdio.h> int main() { int a = -15; int b = a >> 1; printf("%d\n", b);//-8 printf("%d\n", a);//-15 return 0; }
由以上两个例子我们可以得知:右移1位可以理解为除2再向下取整
2.2 左移操作符
左移:左边丢弃,右边补0
#include <stdio.h> int main() { int a = 6; //左移操作符 - 左边丢弃,右边补0 //[00000000000000000000000000000110] - 6的补码 //[00000000000000000000000000001100] - 补码 int b = a << 1; printf("%d\n", b);//12 printf("%d\n", a);//6 //a = a << 1; //a <<= 1; //a = a + 1; //a += 1; return 0; }
以下写法是有问题的:
int main() { int a = 5; int b = a >> -2;//标准未定义行为 return 0; }
3. 位操作符
- & 按位与
- | 按位或
- ^ 按位异或
注: 它们的操作数必须是整数
- & 按位与
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 int c = a & b; //& -- 对应二进制位有0则为0,两个同时为1,才是1 //00000000000000000000000000000011 //11111111111111111111111111111011 //00000000000000000000000000000011 - 补码 printf("%d\n", c);//3 return 0; }
- | 按位或
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 int c = a | b; //| -- 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0 //00000000000000000000000000000011 //11111111111111111111111111111011 //11111111111111111111111111111011 - 补码 //11111111111111111111111111111010 //10000000000000000000000000000101 - 原码 printf("%d\n", c);//-5 return 0; }
- ^ 按位异或
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 int c = a ^ b; //^ - 按(二进制)位异或 - 对应的二进制位相同为0,相异为1 //00000000000000000000000000000011 //11111111111111111111111111111011 //11111111111111111111111111111000 - 补码 //11111111111111111111111111110111 //10000000000000000000000000001000 - 原码 printf("%d\n", c);//-8 return 0; }
接下来我们举一个按位异或的例子:
不能创建临时变量(第三个变量),实现两个数的交换。
首先,我们来复习一下通过创建临时变量来实现两个数的交换:
#include <stdio.h> int main() { int a = 3; int b = 5; printf("交换前: a=%d b=%d\n", a, b); int tmp = a; a = b; b = tmp; printf("交换后: a=%d b=%d\n", a, b); return 0; }
那么不创建临时变量要如何实现呢?
#include <stdio.h> int main() { int a = 3; int b = 5; printf("交换前: a=%d b=%d\n", a, b); /*a = a + b; b = a - b; a = a - b;*/ //但是有缺陷,a和b如果都很大,它们的和放到a里面会放不下(溢出),被截断 a = a ^ b; b = a ^ b; a = a ^ b; printf("交换后: a=%d b=%d\n", a, b); return 0; }
以上代码可能有些难以理解,接下来稍作解释:
int main() { int a = 3; int b = 5; //a^a -> 0 //a^0 = a //异或是支持交换律的 //a^b^a = 5 //011 //101 //110 //011 //101 //a^a^b = 5 return 0; }
4. 赋值操作符
int main() { int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; salary = 20000.0;//使用赋值操作符赋值。 return 0; }
赋值操作符也可以连续使用,比如:
int main() { int a = 10; int x = 0; int y = 20; a = x = y + 1;//连续赋值 return 0; }
同样的语义,还可以这样写:
int main() { int a = 10; int x = 0; int y = 20; x = y + 1; a = x; return 0; }
这样的写法更加清晰爽朗而且易于调试。
赋值操作符中还包括复合赋值符:+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
int main() { int x = 10; x = x + 10; x += 10;//复合赋值,其他运算符一样的道理,这样写更加简洁。 return 0; }
5. 单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
– 前置、后置–
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
- 逻辑反操作
#include <stdio.h> int main() { int flag = 0; if (0 == flag) { printf("hehe\n"); } if (!flag)//flag 为假 打印hehe { printf("hehe\n"); } if (flag) { printf("haha\n"); } return 0; }
- 负值、正值
#include <stdio.h> int main() { int a = 5; int b = -a; printf("%d\n", b); return 0; }
- 取地址、间接访问操作符(解引用操作符)
//& * 应用于指针 #include <stdio.h> int main() { int a = 10; //pa是指针变量 int *pa = &a;//&-取地址操作符-取出a的地址 *pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通过pa中存放的地址,找到指向的空间(内容) int c = *pa; return 0; }
- sizeof
#include <stdio.h> int main() { int a = 10; printf("%d\n", sizeof(int)); printf("%d\n", sizeof(a)); printf("%d\n", sizeof a);//说明sizeof不是函数 return 0; }
#include <stdio.h> int main() { int arr[10] = { 0 }; printf("%d\n", sizeof(arr));//40 - 计算整个数组的大小,单位字节 printf("%d\n", sizeof(int[10])); return 0; }
以下代码输出的分别是多少呢?
#include <stdio.h> void test1(int arr[])//int* { printf("%d\n", sizeof(arr));//4/8 } void test2(char ch[])//char* { printf("%d\n", sizeof(ch));//4/8 } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//40 printf("%d\n", sizeof(ch));//10 test1(arr);//&arr[0] test2(ch);//&ch[0] return 0; }
- 对一个数的二进制按位取反
//~ 按补码二进制位取反 #include <stdio.h> int main() { int a = 0; printf("%d\n", ~a);//-1 //00000000000000000000000000000000 //11111111111111111111111111111111 - 补码 //11111111111111111111111111111110 //10000000000000000000000000000001 - 原码 return 0; }
还记得多组输入要如何表示吗?
#include <stdio.h> int main() { int a = 0; while (1 == scanf("%d", &a)) { printf("%d\n", a); } return 0; }
#include <stdio.h> int main() { int a = 0; //scanf 读取失败返回的是EOF while (scanf("%d", &a) != EOF) { printf("%d\n", a); } return 0; }
其实,也可以用~来表示
#include <stdio.h> int main() { int a = 0; //scanf 读取失败返回的是EOF //假设scanf 读取失败,返回EOF ---> -1 //11111111111111111111111111111111 - 补码 //00000000000000000000000000000000 while (~scanf("%d", &a)) { printf("%d\n", a); } return 0; }
接下来,我们看一个题目(需要结合上面学的一些知识):
把13的二进制的第五位改成1,再改回来
#include <stdio.h> int main() { int a = 13; //00000000000000000000000000001101 //00000000000000000000000000010000 a |= (1 << 4); printf("%d\n", a); //00000000000000000000000000011101 //11111111111111111111111111101111 //00000000000000000000000000001101 a &= (~(1 << 4)); printf("%d\n", a); return 0; }
- 前置、后置++;前置、后置–
#include <stdio.h> int main() { int a = 1; int b = a++;//后置++,先使用,后++ //b=a,a=a+1 printf("a=%d b=%d\n", a, b);//2 1 return 0; }
#include <stdio.h> int main() { int a = 1; int b = a--;//后置--,先使用,后-- //b=a,a=a-1 printf("a=%d b=%d\n", a, b);//0 1 return 0; }
#include <stdio.h> int main() { int a = 1; int b = ++a;//前置++,先++,后使用 //a=a+1,b=a printf("a=%d b=%d\n", a, b);//2 2 return 0; }
#include <stdio.h> int main() { int a = 1; int b = --a;//前置--,先--,后使用 //a=a-1,b=a printf("a=%d b=%d\n", a, b);//0 0 return 0; }
举例:
#include <stdio.h> int main() { int a = 10; printf("%d\n", a++);//10 printf("%d\n", a);//11 return 0; }
- 强制类型转换
#include <stdio.h> int main() { int a = (int)3.14;//强制 printf("%d\n", a); // int a = int(3.14)//err return 0; }
6. 关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,就不过多进行讲解,只需要注意一下==和=不要搞混淆了。
7. 逻辑操作符
&& 逻辑与
|| 逻辑或
//逻辑操作符 //&& || ! - 计算结果是真,使用1表示 #include <stdio.h> int main() { int a = 3 && 5; printf("%d\n", a);//1 return 0; }
#include <stdio.h> int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); //a 和 b 都是5 打印hehe if (5 == a && 5 == b) { printf("hehe\n"); } //a 或者 b是5 打印haha if (5 == a || 5 == b) { printf("haha\n"); } return 0; }
判断闰年:
#include <stdio.h> int main() { int y = 0; scanf("%d", &y); //1. 能被4整除,并且不能被100整除 //2. 能被400整除是闰年 if (((0==y%4)&&(y%100!=0)) || (0==y%400)) { printf("闰年\n"); } else { printf("不是闰年\n"); } return 0; }
例题:
//&& 操作符,左边为假,右边就不计算了 //|| 操作符,左边为真,右边不再计算 #include <stdio.h> int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; i = a++ && ++b && d++; //i = a++||++b||d++; printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d); return 0; }