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