c语言提供了各种各样的操作符,可大致分为以下类型:
1. 算术操作符
2. 移位操作符
3. 位操作符
4. 赋值操作符
5. 单目操作符
6. 关系操作符
7. 逻辑操作符
8. 条件操作符
9. 逗号表达式
10. 下标引用
11. 函数调用和结构成员
一:算术操作符
算术操作符大家都很熟悉,只需注意以下几点即可:
1. 对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
2. %操作符的两个操作数必须为整数。
二:移位操作符
移位操作符分为左移和右移,右移又分为逻辑右移和算术右移,为啥要分成这两种呢,因为在右移过程中左边到底是补0还是补符号位,所以有了逻辑和算术右移,那怎样来具体使用哪种呢,这里是没有办法的,右移取决于你的编译器,具体哪种你可以用自己编译器试试。(注意:目前基本上所有编译器都是算术右移,想想也只有算术右移更合理嘛)
注意:某一个数在只进行移位操作时不会改变本身的值。(例如:a<<1,单单这一条语句a的值没发生任何变化;a = a<<1,a才得到了它左移一位的值)
举例:
int a = -1;
a = a >> 1;
printf("%d", a);
return 0;
来做个简单的分析:
提示:所有数在计算机中都是以补码的形式储存,所以移位操作也是在补码的形式上进行的。
-1的源码为(32位编译器):1000 0000 0000 0000 0000 0000 0000 0001
-1的反码为(32位编译器):1111 1111 1111 1111 1111 1111 1111 1110 (源码基础上符号为不变,其余位取反)
-1的补码为(32位编译器):1111 1111 1111 1111 1111 1111 1111 1111 (反码基础上加一)
右移一位,左边补上符号为:1111 1111 1111 1111 1111 1111 1111 1111
这是补码,我们再反转回去变到源码,再输出,结果为:
这只是移位操作最基础的知识,移位再结合其他运算可以解决很多问题,后面我们具体分析。
三:位操作符
位操作符有:
按位与(&):都为一为一,否则为0
按位或(|):都为零为零,否则为一
按位异或(^):相同为零,不同为一
实例1:交换两个数,不创建临时变量(直接上代码)
int a = 1;
int b = 2;
a = a^b;
b = a^b;
a = a^b;
记忆技巧,a^b相当于一个中间转换站,谁和它异或就会得到另一个数;
实例二:找出数组:123452345中唯一单的那个数
解:将所有数字异或在一起,结果就是那个单的数
四:单目操作符
! 逻辑反操作 (对一个数的所有二进制位取反)
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置-
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
这里面很多大家都常见,sizeof出现在这里有没有很意外?对,他就是个操作符,不是个函数,下面一段代码可以细细品尝,运行结果已经写在上面:
int a = 1;
char b = 1;
char c = 10;
printf("%d\n",sizeof a); //4 一方面证明了sizeof不是函数,所以后面可以没有()
//printf("%d",sizeof int); //错误语句 当后面是类型时必须加()
printf("%d\n",sizeof (int)); //4
printf("%d\n",sizeof (c=b+3)); //1
printf("%d\n",c); //10 证明了sizeof不是函数,不会计算出结果
再介绍一个奇怪的题(不必过于纠结,不同编译器可能会出现不同的值,我的DEV运行结果为十)
int i = 1;
int ret = (++i) + (++i) +(++i);
printf("%d\n",ret);
printf("%d",i);
五:逻辑操作符
逻辑操作符分为: 逻辑与 && 逻辑或 ||
具体需要注意的地方,我们来看一个典例:
int i = 0;
int a = 0;
int b = 2;
int c = 3;
int d = 4;
// i = a++ && ++b && d++;
// printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d); //1 2 3 4
i = a++ || ++b || d++;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d); //1 3 3 4
return 0;
可以看到逻辑与与逻辑或满足短路原则,左边表达式的真与假可决定右边是否再执行。
六:条件操作符
exp1 ? exp2 : exp3
实用代码:
int a = 2;
int b = 5;
int max = a>b?a:b;
printf("max=%d\n",max);
return 0;
该程序一目了然,实现了两个数求较大值,当然也可以求较小值;
七:逗号表达式
exp1, exp2, exp3, …expN
逗号表达式遵循从左向右依次执行,最终结果取决于最右边的表达式;
下面看一段代码,结果在注释里:
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
printf("c=%d\n",c); //13
if (a>100, b>0, a>10) //此时a的值为12
printf("yes\n"); //会输出yes
return 0;
注意:逗号表达式取决于最右边,但左边的都会依次执行 ?
八:下表引用操作符
符号为:[ ]
大家对这个符号很熟悉吧,在数组里经常用到它,例如a[3],因为它是个操作符,所以也可写成3[a],但最好不要这样写,大大降低了程序的可读性。
九:访问一个结构的成员
访问一个结构体成员有两种方式:
1. . 操作符 结构体.成员名
2. ->操作符 结构体指针->成员名
看一段例程代码:
#include<stdio.h>
#include<string.h>
struct stu
{
int age;
char name[20];
char sex[5];
};
int main()
{
struct stu student;
struct stu* pstu = &student;
student.age = 20;
strcpy(student.name , "zhangsan");
strcpy(student.sex , "男");
printf("%s\n",student.name); //直接引用
printf("%s\n",pstu->name); //指针引用
return 0;
}
当结构体作为函数传参时,我们最好用结构体指针作为形参,在函数内用->来调用成员,因为如果用结构体名作为形参,这样函数传参时会把结构体所有内容都拷贝一份,如果结构体内容很大,则会浪费许多空间,降低运行效率;