小结 | C语言操作符
目录:
C语言操作符分类总结:
C语言中大致分为以下几类:
- 算数操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用、结构成员访问操作符
** 注意:**
其中,移位操作符和位操作符都是对二进制序列进行操作。
=具体分析=
一、算数操作符
+ - * / %
这里需要注意的是:
1.除法(/)
整数除法时,左右两边都需要为整数;
而浮点数除法时,左右两边至少有一个数为浮点数。
2.取模运算(%)
只能对整数操作。
二、移位操作符
<<左移操作符
>>右移操作符
移动规律:
左移:左边丢弃,右边补0;
右移:
1.算数右移:右边丢弃,左边补符号位(正数补0,负数补1);
2.逻辑右移:右边丢弃,左边补0;
大小的变化:
左移:每左移一位,相当于乘2;
右移:每右移一位,相当于除2。
三、位操作符
- & 按位与 (两数二进制位都为1 —>1)
- | 按位或 (两数二进制位有一个为1 —>1)
- ^ 按位异或 (两数二进制位不同为1,相同为0)
关于异或的应用例题:
在一个是数组中,只有一个数字单独出现,其余都两两出现,求单独出现的数字。
int ret = 0;
for(i = 0; i < n; i++){
ret = ret ^ a[i];
}
异或(^)的特点:
1.两数相同,异或为0;
2.0与任何数异或,还是这个数。
这里有一道关于异或的题,是上一题的升级版解题思路较巧妙,有兴趣的可以看下:
题目:一个数组中只有两个数字是出现一次,其他所有数字都出现了两次,找出这两个数字,编程实现。
#include <stdio.h>
void search_one(int arr[],int len)
{
int data = arr[0];
int i = 1;
for (; i < len; i++) {
data ^= arr[i];
}
//异或结果为 data = m ^ n; (假设单独出现的两数为m、n)
//由于m、n为单独出现的两数,所以m一定不等于n,即data一定不为0,data的二进制位至少有一位为1
int flag = 1;
for (i = 0; i < sizeof(int)*8; i++) {
if (data & (flag<<i) ) {
flag <<= i;
break;//可不加break,因为用flag记录哪一位都可以
}
}
//找出m与n相异的一位,即data二进制位为1的一位
//用flag来记录是哪一位,在这里我们假设是第x位
int dataA = 0;//组A
int dataB = 0;//组B
for (i = 0; i < len; i++) {
if (arr[i] &flag) {
dataA ^= arr[i];
}
else{
dataB ^= arr[i];
}
}
/*在这里我们将整个数组的数分成两组,分组条件是,第x位为1的在一组里,为0的在一组里,可得出两点结论
1.m、n肯定不在一组里
2.重复出现的两个数一定在一组里*/
printf("%d %d\n",dataA,dataB);
}
int main(int argc, const char * argv[]) {
int a[] = {1,1,2,8,2,3,3,4,4,5,6,5};
search_one(a,12);
return 0;
}
四、赋值操作符 =
赋值操作符可以与其他操作符连用,形成复合赋值符,
如:
+= , -= , *= , /= , %= , >>= , << , &= , |= , ^=
用法:
a += 2;
等同于 a = a + 2;
其余类似。
五、单目操作符
- ! 取反
- -- 负值
- + 正值
- & 取地址
- sizeof 计算字符串长度(以字节为单位)
- ~ 对二进制取反
- -- 前置、后置--
- ++ 前置、后置++
- * 间接操作符(解引用操作符)
- (类型) 强制类型转换
单目操作符即只有一个操作元素(可以看到,前面几个操作符都有两个操作元素);
这里需要注意的是,sizeof这个操作符,
int a = 10;
sizeof (a)
——这里括号可以省略,即写成 sizeof a
也可以;
但是sizeof(int)
这种情况下括号不可省略;
另外和数组结合时,也需要注意:
int arr[3] = {1,2,3}
sizeof(arr)
结果为12(arr表示整个数组)
sizeof(arr+0)
结果为4(表示首元素地址)
下面再来看一段关于sizeof的代码:
int a = 1;
short s = 3;
printf("%d\n",sizeof(s=a+5));
printf(""%d\n",s);
结果为:
2
3
可能会有人有疑问,明明 s = a + 5 赋值给了s,为什么s的值还是3?
这里有一点需要记住,放在sizeof内部的表达式不参与运算,因为关于sizeof的运算是在编译期间就完成了的,而我们所讲的sizeof运算,需要在运行期间完成,即在运行前,sizeof内部的值已经被替换成了2(sizeof最终看s的类型),代码相当于
int a = 1;
short s = 3;
printf("%d\n",sizeof(2));
printf(""%d\n",s);
那s的值也就不会发生变化,输出时仍为3。
六、关系操作符
>= , <= , > , < , != , ==
七、逻辑操作符
&& 逻辑与
|| 逻辑或
需要与位运算(按位与&、按位或|)区分开来。
下面是一道关于逻辑与经典的例题:
int i = 0 , a = 0 , b = 2 , d = 4;
i = a++ && ++b && d++;
printf("%d %d %d\n",a,b,d);
结果为 1 2 4
可以看到,a先参与了运算,再自增,但是参与运算时它的值为0,逻辑与(&&)操作符的运算特点是,全为1结果才为1,当出现了一个0时,后面的他将不会继续运算下去,因此,++b和d++并未实现,所以结果只有a加了1.
再看另一个操作符:
int i = 0 , a = 0 , b = 2 , d = 4;
i = a++ || ++b || d++;
printf("%d %d %d\n",a,b,d);
结果为 1 3 4
逻辑或的运算特点是,当所有值都为0时,结果才为0,当a参与运算时为0,||会继续看后面的运算,当++b的值不为0时,无论后面的值是多少,其结果已经是1了,后面的d++也就不会运算下去了。
八、条件操作符(三目操作符)
exp1 ? exp2 : exp3
当exp1的值为真时,执行exp2表达式,反之,执行exp3表达式;
例:
求a,b中最大值可以这么求
int max = a>b ? a : b;
九、逗号表达式
exp1,exp2,exp3,...,expN
从左向右依次执行,最终结果为expN。
十、下标引用、函数调用、结构成员访问操作符
-
下标引用操作符即 [ ],经常在数组中能看到,
例如int arr[10] = {0};
当赋值arr[5] = 2;
相当于执行了*(arr+5)= 2;
这样一个操作。 -
函数调用操作符 (), 在函数中经常能看到。
-
结构成员访问操作符,分为两类:
.
和->
当我们定义一个结构体
struct Stu{
char name[20];
char id[13];
int age;
};
这时候只是定义,创造了一个类型,注意这个时候并没有给他分配空间,类似于创造了int这样一个类型。
而当我们用时,
struct Stu s;//这相当于我们 int a;(开辟了空间)
strcpy(s.name,"张三");
strcpy(s.id, "201606060501");
s.age = 20;
在这里s.name中的.
就是成员访问操作符。
但是另一种情况,
struct Stu *p = &s;
如果这时候需要通过指针p来修改结构体成员的值,
则:
strcpy((*p).name,"张三");
strcpy((*p).id, "201606060501");
(*p).age = 20;
但是(*p).
这种写法复杂繁琐,于是有了另一个操作符->
,它的用法如下:
strcpy(p->name,"张三");
strcpy(p->id, "201606060501");
p->age = 20;
哈哈哈哈哈哈