c语言操作符详解
c语言的操作符有以下几大类
- 算数操作符
- 位移操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标应用,函数调用和结构成员
1.算术操作符
1| + - * / %
- 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
- / 操作符是取商,% 操作符是取余
- 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。(例如:int a= 3 / 5 ,a结果为0 , 但 int a = 5.0 / 3 或 int a = 5 / 3.0 , a 的结果依然为0。如果想要小数位上有值就要写成double a = 5.0 / 3 或 flout a = 5 / 3.0)
- % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
2.位移操作符
注:移位操作符的操作数只能是整数。
1| << >>
举个例子
int a = 1;
int b = a<<1;//向左移动一位
打印出来b的结果是2。
其实这里的移位移的是二进制位,例子中的a在内存中的存放是 :
00000000000000000000000000000001
左移一位后得到的b结果为
00000000000000000000000000000010
这里可以看出左移操作符的特点为:左边丢弃,右边补零
再举一个例子
int a = -1;
int b = a>>1//向右移动一位
这里打印出来b的结果还是-1
其实右移操作符有两种右移方法
1.算数右移(正负数都通用)
右边丢弃,左边补原符号位
2.逻辑右移(正数)
右边丢弃,左边补零
所以上面代码a在内存中的存放为:
原码:10000000000000000000000000000001
反码:1111111111111111111111111111111111110
补码:1111111111111111111111111111111111111
所以右移一位,左边补符号位,右边丢弃,得到的结果为
补码:1111111111111111111111111111111111111
所以-1右移一位得到的结果b还是-1
注意a向右移动,是把a向右移动之后的值赋给了b,这里的a没有发生改变它的值还是之前a赋的值
补充
正数与负数在内存中的存放方式都是以补码的方式存放(正数的原码,反码,补码都相同)
负数的符号位就是在二进制数列的第一位写1
正数的符号位是0
整数二进制的表达形式其实有三种
原码:直接根据数值写出二进制序列就是原码
反码:原码的符号位不变,其它的按位取反就是反码
补码:反码+1,就是补码
3.位操作符
& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数。
举例说明:
&按(二进制)位与
int a = 3;
int b = 5;
// & - 按(二进制)位与
int c = a & b;
a 的二进制为 00000000000000000000000000000011
b 的二进制为 00000000000000000000000000000101
c 的二进制为 00000000000000000000000000000001
如果a,b对应二进制位相同那么输出 c 的二进制对应位不变,如果a,b二进制位不同那么输出 c 的对应二进制位为0。所以 c 的值为1
| 按(二进制)位或
如果 int c = a | b;
对应的二进制位只要有1则输出为1,只有两个都为0结果才为0;故结果为00000000000000000000000000000111;故c的值为7
^ 按(二进制)位异或
如果 int c = a ^ b;
对应的二进制位相同则为0 ,相异则为1;故结果为00000000000000000000000000000110;故c的值为6
可是这个操作符到底有什么用呢???
先来看一道题:
创建两个变量int a = 3; int b = 5。要求不能创建一个新的变量,并且交换他们两个的值
这里你又想到了什么方法呢?
先用一个以往的方法
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
a = a + b;//a=8
b = a - b;//b=3
a = a - b;//a=5
printf("a=%d,b=%d",a,b);
}
得到的结果
但是这样写有一个缺陷,如果a赋一个很大的值,b赋一个很大的值,a+b可能会超出int整形表示的最大值
所以我们使用刚刚学到的那种方法
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
a = a ^ b;//a=8
b = a ^ b;//b=3
a = a ^ b;//a=5
printf("a=%d,b=%d",a,b);
}
得到的结果
这里的b可以写成 b = a ^ b ^ b ;两个相同的数异或一定为0,那b就等于a ^ 0,并且0^任何数都等于它本身,那b就等于a的值了
4.赋值操作符
= += -= *= /= >>= <<= %= &= |= ^=
复合赋值:
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。
需要注意的是: ‘=’ 一个等号才叫赋值,两个等号叫判断相等
5.单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
!逻辑取反
来看两张图来理解
可以看出这里的 !逻辑取反意思就是把真变假,把假变真
sizeof 计算所占空间的大小
可以写成
#include <stdio.h>
int main()
{
int a = 3;
printf("%d",sizeof(a));
printf("%d",sizeof(int));
printf("%d",sizeof a );
}
输出结果为
这里的 printf(“%d\n”,sizeof a ) a可以不加括号说明 sizeof 不是函数,是一个操作符
再来举一个例子:
#include <stdio.h>
int main()
{
short s = 5;
int a = 10;
printf("%d\n",sizeof(s = a + 5));
printf("%d",s);
}
先想一下这道题的答案再看下面的结果
这里 s = a + 5 是把 a+5 的值赋给了 s 所以 sizeof 输出的结果还是一个短整型 short 所占的的内存,还可以看出 sizeof 括号内的值不参与运算所以下面 s 的输出值没有改变
~ 对一个数的二进制按位取反
例如 -1 在二进制中的存放
原码:10000000000000000000000000000001
反码:11111111111111111111111111111110
补码:11111111111111111111111111111111
所以 -1 按位取反后为
00000000000000000000000000000000
得到的 ~-1 的结果为 0
- - ++
举个例子
int a = 3;
int b = 5;
int c = --a;
int d = b++;
printf("%d %d %d %d",a,b,c,d);
输出结果为
这里的 ++a 是先 运算再赋值;–a是先 赋值再运算
&取地址操作符与 * 间接访问操作符(解引用操作符)
这里看下面的代码就可以很好的理解了
(类型) 强制类型转换
看图来理解
这里可以看到在编译时出现的问题,但如果在3.14前加上一个括号来转换类型就不会出现下面的提示了
6.关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,,但是我们要注意一些运算符使用时候的陷阱。
1 . = 是赋值,== 是判断相等。
2 . 比较字符串不能使用 == 来判断
7.逻辑操作符
&& 逻辑与
|| 逻辑或
他们都是用来判断真假的
&&:(A 和 B都为真,结果才为真,其中一个为假,结果便为假)
|| :(A 和 B其中一个为真,结果就为真,两个都为假,结果才为假)
这里给大家出一道 && 的题:
先自己做完再往下看
#include <stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("%d %d %d %d",a, b, c, d);
return 0;
}
结果为:
这里的 i = a++ && ++b && d++;是先判断a++,但a++是先赋值再运算,这里赋值为 0 了,所以后面的 ++b && d++ 就没有运算
再来一到 || 的题:
#include <stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("%d %d %d %d", a, b, c, d);
return 0;
}
结果为:
这里的i = a++ || ++b || d++;,判断a++为 0 - 假 ,继续执行 ++b ,++b为真,停止运行,所以结果输出为1 3 3 4
总结:若&&运算符左边表达式为假(0),则其右侧表达式不再计算,整个表达式必然为假;若||运算符左边的表达式为真(非0的值)则其右侧表达式不再计算,整个表达式为真
8.条件操作符
exp1 ? exp2 : exp3
这里的意思就是如果exp1成立(为真)就执行exp2,否则执行exp3
9.逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,从左向右依次计算,但是整个表达式的结果是最后一个表达式的结果
举个例子来理解:
int main()
{
int a = 3;
int b = 4;
int c = 0;
int d = (c = 5, a = b + c, b = c + a);
printf("%d", d);
}
这道题d的结果为14
这里需要注意的是前面的表达式虽然不打印,但也要计算,一定要注意不能直接计算最后一个表达式!
10.下标应用,函数调用和结构成员
1. []下标引用操作符
看下面代码来理解
arr【5】是把数组中第六个元素改变了
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
举例:
// 函数的定义
int Add(x,y)
{
return x+y;
}
void text()
{}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);// 这个括号便是叫函数调用操作符;
text();//即使不传参也要加括号
return 0;
}
3. 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
举例:
假如这里有一本书:书名,书号,价格
创建一个自定义的类型
#include <stdio.h>
struct book
{
char name[20];
char id[20];
int price;
};
int main()
{
struct book a = { "c语言","c54564546",30 };
printf("%s\n", a.name);
printf("%s\n", a.id);
printf("%d\n", a.price);
struct book* pa = &a;
printf("%s\n", (*pa).name);
printf("%s\n", (*pa).id);
printf("%d\n", (*pa).price);
printf("%s\n", pa->name);
printf("%s\n", pa->id);
printf("%d\n", pa->price);
}
输出结果