目录
一、操作符的分类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
在下文中我们会依次讲解这些操作符。
二、算术操作符
+ - * / %
在这里我们主要讲解/与%操作符。
2.1 / 操作符
对于 / 操作符,我们主要注意以下 :
- 对于/操作符,如果两个操作数都为整数,执行整数除法。如5/2=2;
- 如果两个操作数中有浮点数,执行的就是浮点数的除法。如5.0/2=2.5;
2.2 % 操作符
%操作符的两个操作数都必须是整数,他求的是整除之后的余数。
三、移位操作符
<< 左移操作符
>> 右移操作符
移位操作符移的是二进制的位。
在计算机中,整型的二进制表示包括三种:原码、反码、补码。
我们分别使用15与-15来举例讲解其原码、补码以及反码。
15:
-15:
注意:
- 在计算机内存中存储的是数据的补码。
- 在打印数据时,打印的是数据的原码结果。
3.1 左移操作符
左移操作符的移位规则是:
左边抛弃,右边补0:
3.1.1 正整数的左移
#include <stdio.h>
int main()
{
int a = 4;
int b = a << 1;
printf("a = %d b = %d\n", a, b);
return 0;
}
接下来我们分析a<<1的结果:
在上面的分析中,我们得知,进行移位操作后,b = 8, 那么a的值是否会发生变化呢?答案是不会。
注意,a<<1时,a的值不会发生变化。
3.1.2 负整数的左移
对于负数,在左移时相对复杂,我们举例来观察:
#include <stdio.h>
int main()
{
int a = -4;
int b = a << 1;
printf("a = %d b = %d\n", a, b);
return 0;
}
我们发现:
- 左移一位有*2的效果,即b = a<<1;相当于b = a*2;
- b = a<<1;左移不会改变a的值。
3.2 右移操作符
右移操作符的移位规则:
首先右移运算分为两种:
- 逻辑移位 左边用0填充,右边丢弃。
- 算术移位 左边用原该值的符号位填充,右边丢弃。
至于两种右移方式选择哪一种主要是看编译器,在大多数的编译器中采用算术移位。
我们来举例说明:
#include <stdio.h>
int main()
{
int a = -4;
int b = a >> 1;
printf("a = %d b = %d\n", a, b);
return 0;
}
上述代码结果为:
注意:对于移位操作符,不要移动负数位,这个是标准未定义的。
四、位操作符
位操作符有:
& 按位与(对应的二进制位按位与,如果有0则为0,两个同时为1才为1)
| 按位或(对应的二进制位按位与,如果有1则为1,两个同时为0才为0)
^ 按位异或(对应的二进制位按位异或,如果相同为0,不同为1)
注意,以上三个操作符的操作数都必须为整数。
上述的三种运算符都是对补码进行操作。
我们来通过具体的示例来讲解:
4.1 & 按位与
int main()
{
int a = 3;
int b = -5;
int c = a & b;
printf("c = %d\n", c);
return 0;
}
这里再次重申在内存中计算的时候是对补码进行运算,但是打印的时候,打印的是原码对应的十进制值。
4.2 | 按位或
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a | b;
printf("c = %d\n", c);
return 0;
}
4.3 按位异或
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;
printf("c = %d\n", c);
return 0;
}
4.4 一道相关面试题
不能创建临时变量(第三个变量),实现两个数的交换
方法一:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a + b;
b = a - b;
a = a - b;
printf("a = %d b = %d\n", a, b);
return 0;
}
上述的方法存在一定的缺陷,如果a,b中存放的值都特别大,但是没有超出int类型的范围,那么a+b的值就会超过int类型的范围,会有一些数据在计算中丢失。
改进后的代码:
首先我们需要知道按位异或的几个特点:
- 任何的数字与0异或,得到的结果依然是他自己。
- 0与a异或,结果为a。
- a与a异或,结果为0。
- 异或支持交换律。
#include <stdio.h>
//3^3=0
//5^5=0
//3^5=6
//3^5^5=3
//3^5^3=5
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d b = %d\n", a, b);
return 0;
}
五、赋值操作符
例如:
int weight = 120;
5.1 复合赋值符
+= a+=5;相当于a=a+5;
-= a-=5;相当于a=a-5;
*= a*=5;相当于a=a*5;
/= a/=5;相当于a=a/5;
%= a%=5;相当于a=a%5;
>>= a>>=5;相当于a=a>>5;
<<= a<<=5;相当于a=a<<5;
&= a&=5;相当于a=a&5;
|= a|=5;相当于a=a|5;
^= a^=5;相当于a=a^5;
六、单目操作符
6.1 单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置--,后置--
++ 前置++,后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
6.2 sizeof操作符
注意:sizeof()内部存放的表达式是不会计算的。
例如:
int main()
{
short s = 10;
int a = 2;
printf("%d\n", sizeof(s = a + 5));
printf("%d\n", s);
return 0;
}
运行结果是:
为什么会是上述结果呢?
sizeof(s = a+5) sizeof() 在()内部的这个表达式是不计算的,因为只要它是一个表达式,一定能计算出一个结果,同时他的类型可以推导出来,不需要再进行计算,s=a+5这个表达式的结果由s决定,s是short类型的,short类型所占的字节数为2,所以 sizeof(s = a+5)的结果就是2,而由于sizeof() 在()内部的这个表达式是不计算的,所以s依然等于10.
6.3 & 与 * 操作符
在创建变量的时候,我们需要在内存中开辟一块内存空间,开辟空间就会有地址。
在为变量开辟空间之后,我们可以通过&操作符知道变量的地址。
在知道变量地址的情况下,想要访问变量中的元素,我们可以使用*(解引用)操作符。
下面通过具体的代码示例来看:
#include <stdio.h>
int main()
{
int a = 3;
int* pc = &a; //& - 取地址操作符
printf("&a = %p\n", &a);
*pc = 5; //解引用操作符 找到地址指向的变量
return 0;
}
七、关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
注意:==不能比较两个字符串的内容,他实际上比较的是2个字符串的首字符的地址。
八、逻辑操作符
&& 逻辑与(两个操作数同时为真,才为真)
|| 逻辑或(两个操作数中有一个为真,就为真)
在逻辑操作符计算出的结果,如果计算为真就1,如果计算为假就为0。
举例:
上述条件中第一个a&&b成立,会打印;第二个a&&b不成立,不会打印。
在这里,对于这两个操作符我们需要注意的点是:&&与||可能会出现短路的情况。
if(a&&b) 如果a为假的时候,就不会判断b了,a为假的时候就已经知道表达式的结果了。
if(a||b)如果a为真的时候,就不会去判断b了,a为真的时候就已经知道表达式的结果了。
我们来看几道典型的例题:
#include <stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
return 0;
}
他的输出结果是什么呢?
为什么会是这样呢?下面我们来进行解释:
九、条件操作符
exp1 ? exp2 :exp3
条件操作符,如果表达式1(exp1)的结果为真,表达式2(exp2)的结果是整个表达式的结果,如果表达式1(exp1)的结果为假,表达式3(exp3)的结果是整个表达式的结果。
代码举例:
int main()
{
int a = 3;
int b = 0;
if (a > 5)
{
b = 3;
}
else
{
b = 9;
}
}
上述的代码相当于:
int main()
{
int a = 3;
int b = 0;
b = ((a > 5) ? 3 : 9);
}
十、逗号表达式
exp1,exp2,exp3,...... expN
逗号表达式就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
代码举例:
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
//c=13,从左向右依次计算,a > b, a = b + 10 = 12, a, b = a + 1 = 13
}
十一、下标引用、函数调用和结构成员
11.1 [ ]下标引用操作符
[ ]的操作数:一个数组名 + 一个索引值
注意:
int arr[10]; //创建数组
arr[9] = 10; //实用下标引用操作符
[ ]的两个操作数是arr和9.
11.2 ()函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
例如:
void text(int x, int y)
{
printf("%d %d",x,y);
}
int main()
{
text(3,4);
}
在上述的代码中,text(3,4); ()就是函数调用操作符,他的操作数是text,3, 4。
11.3 访问一个结构体成员
. 结构体.成员名
-> 结构体指针->成员名