前言
本篇分享的是部分操作符的概念与用法,从经典例题入手,带你快速了解和掌握。
收录专栏:浅谈C语言
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
包括 + - * / %
例如:计算 3/2
#include
int main()
{
int a=3/2;
printf("%d\n",a)
return 0;
}
如果你觉得这样写,就大错特错了。
运行结果:
这是因为这里的 a 是整型的,计算出的结果就是整型,会自动舍去小数点后的数字。
更改 a 的类型为浮点数:
int main()
{
double a = 3 / 2;
printf("%lf\n", a);
return 0;
}
其实,这样也是不对的。
运行结果:
仔细观察就会发现,这里的 a 虽然是 double 类型的,但 3 和 2 都是整形的, 3/2 计算后是 1,然后再将 1 装换成 double 型的赋给 a。
所以当计算的结果会出现浮点数时,除号的两端只要有一个小数就可以。
int main()
{
//下面三种方式都可以计算出 1.5
double a = 3.0 / 2.0;
//double a = 3.0/2;
//double a = 3 / 2.0;
printf("%lf\n", a);
return 0;
}
注:
除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数
对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法
除法中,除数不可以为 0
% 操作符的两个操作数必须为整数。返回的是整除之后的余数
<< 左移操作符
>> 右移操作符
移位操作符的操作数只能是整数。
移动的是二进制,因为能够处理的是二进制的信息
移位规则:左边抛弃、右边补0
例如:
#include
int main()
{
int a = 15;
printf("%d\n",a<<1);//移动就是a中的2进制信息
return 0;
}
图解:
运行结果:
移位规则:
首先右移运算分两种:
- 逻辑移位
左边用0填充,右边丢弃- 算术移位
左边用原该值的符号位填充,右边丢弃
在C语言没有明确规定倒是算术右移还是逻辑右移,一般编译器上采用的是算术右移。
例如:
int a = -1;
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int a = 15;
a>>-1;//error
移位移动的是补码的二进制序列
//tip:
int a = 15;
//00000000000000000000000000001111 - 原码
//00000000000000000000000000001111 - 反码
//00000000000000000000000000001111 - 补码
int b = -15;
//10000000000000000000000000001111 - 原码
//11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码)
//11111111111111111111111111110001 - 补码(反码+1就是补码)
//整数在内存中存储的是补码
//计算的时候也是使用补码计算的
位操作符有:(也是操作二进制位)
& //按位与
| //按位或
^ //按位异或
上述的操作数必须是整数。
例如:
// & 操作符
#include
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
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 - -5
printf("%d\n", c);//-5
return 0;
}
// ^ 操作符
#include
int main()
{
int a = 3;
//00000000000000000000000000000011 - 补码
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 - 补码
//
int c = a ^ b;
//^ - 按二进制位异或 -对应的二进制位相同为0,相异为1
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111000 - 补码
//11111111111111111111111111110111
//10000000000000000000000000001000 - -8
printf("%d\n", c);//-8
return 0;
}
实例1:交换2个整型变量
要想交换两个变量的值,最常见的方法是再创建一个变量,帮助我们实现:
#include
int main()
{
int a = 3;
int b = 5;
//交换
int tmp = a;//临时变量 tmp
a = b;
b = tmp;
printf("交换前:a=%d b=%d\n", a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
运行结果:
如果不创建临时变量(第三个变量),可以实现两个整数的交换吗?
回答是可以的
方法一:
#include
int main()
{
int a = 3;
int b = 5;
a = a + b;
b = a - b;
a = a - b;
printf("交换前:a=%d b=%d\n", a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
运行结果:
方法二:使用 ^ 操作符实现
#include
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;
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
运行结果:
//a^a -> 0
//a^0 = a
#include
int main()
{
int a = 3;
int b = 5;
int c= a^b^a;
int d= a^a^b;
printf("%d %d\n", c, d);
return 0;
}
同样地,a^a^b=a
运行结果:
实例2:求一个整数存储在内存中的二进制中1的个数。
//方法1
#include
int main()
{
int num = 10;
int count = 0;//计数
while (num)
{
if (num % 2 == 1)
count++;
num = num / 2;
}
printf("二进制中1的个数 = %d\n", count);
return 0;
}
//方法2:
#include
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
for (i = 0; i < 32; i++)
{
if (num & (1 << i))
count++;
}
printf("二进制中1的个数 = %d\n", count);
return 0;
}
//方法3,优化版本
#include
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
while (num)
{
count++;
num = num & (num - 1);
}
printf("二进制中1的个数 = %d\n", count);
return 0;
}
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续使用
例如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1;
a = x;
可见,下面的写法更加清晰爽朗而且易于调试。
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。
例如:
int x = 10;
x = x+10;
x += 10;//复合赋值,与上面等式等价
//但下面复合语句明显更加简洁。
其他运算符也是一样的道理。
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
单目操作符只有一个操作数
实例1: !
// ! 操作符
//非 0 为真
#include
int main()
{
int flag = 5;
if (flag == 0)
{
printf("hehe\n");
}
if (!flag)//这里当 flag 为假时,才会打印hehe
{
printf("hehe\n");
}
if (flag)
{
printf("haha\n");
}
return 0;
}
运行结果:
实例2: -
// - 操作符
#include
int main()
{
int a = 5;
int b = -a;
printf("%d\n", b);
return 0;
}
运行结果:
实例3: &
// & * 操作符
#include
int main()
{
int a = 10;
//pa是指针变量
int* pa = &a;//&-取地址操作符-取出a的地址
*pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容)
//同时也更改了a 的内容
int c = *pa;
printf("a=%d c=%d\n",a, c);
return 0;
}
运行结果:
实例4:在计算数据类型所占内存大小时,可以使用 sizeof 操作数
sizeof不是函数,是操作符
sizeof计算的是类型创建变量的大小,单位是字节
//sizeof 操作符:
#include
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);
//printf("%d\n", sizeof int);//不允许这样计算,说明 sizeof 不是函数
return 0;
}
实例5: ~
// ~ 按补码二进制位取反
#include
int main()
{
int a = 0;
printf("%d\n", ~a);
//00000000000000000000000000000000
//11111111111111111111111111111111 - 补码
//11111111111111111111111111111110
//10000000000000000000000000000001 -1
return 0;
}
运行结果:
实例6:前置 ++ 、 后置 ++
//前置++:先对变量进行自增,然后使用该变量
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;
}
运行结果:
//后置++:先对a先使用,再自增
#include
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
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;
}
运行结果:
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
编写过程中 == 和 = 不要写错
实例:判断闰年
#include
int main()
{
int y = 0;
scanf("%d", &y);
//1. 能被4整除,并且不能被100整除
//2. 能被400整除是闰年
if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
{
printf("闰年\n");
}
else
{
printf("不是闰年\n");
}
return 0;
}
&& 逻辑与--并且
|| 逻辑或--或者
1&2----->0
1&&2---->1
// & 两边二进制补码计算,相同为1,不同为0
// && 两边同为真,结果也是真,用 1 表示;只要有一个为假,结果就是假,用 0 表示
1|2----->3
1||2---->1
// | 两边二进制补码进行计算,只要有一个 1 就是 1;当两边都为 0 时,才是0
// || 两边只有一个真时,就为真;都是假时,才为假
实例1:
// &&
#include
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);
return 0;
}
运行结果:
实例2:
// ||
#include
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++||++b||d++;
printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);
return 0;
}
运行结果:
exp1 ? exp2 : exp3
实例:
//如果 a>5,则 b=3
//如果 a<=5,则 b=-3
if (a > 5)
b = 3;
else
b = -3;
上述代码可以转换成条件表达式:
#include
int main()
{
int a ;
scanf("%d", &a);
int b = a > 5 ? 3 : -3;
printf("%d\n", b);
return 0;
}
运行结果:
使用条件表达式实现找两个数中较大值
#include
int main()
{
int a, b ;
scanf("%d %d", &a, &b);
int c = a > b ? a : b;
printf("%d\n", c);
return 0;
}