首先说一下常见的操作符分类:
- 算数操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
下面对它们做详细的介绍:
算术操作符
+ - * / %
+
-
*
与正常运算相同,在这不做过多介绍。/
操作符,如果两个操作数都为整数,那么,执行整数除法,如果两个操作数中有一个为浮点数,那么执行的就是浮点数的除法。%
操作符的两个操作数必须为整型。
下面我们对上面说到的情况做一演示:
如果将%
操作符的一个操作数改为非整型:
移位操作符
<< 左移操作符 >> 右移操作符
- 左移操作符
<<
:
规则:左边丢弃,右边补0
。
举个栗子:
右移操作符
>>
:
规则:- 逻辑移位:
左边补0, 右边丢弃。 - 算术移位:
左边补符号位,右边丢弃。
举个栗子:
- 逻辑移位:
移位操作符使用的过程中还需要注意一点:对于移位操作符来说,不能移动负数位,这个在标准中是未定义的。
举个栗子:
位操作符
& 按位与
| 按位或
^ 按位异或
注意:
- 它们的操作数必须为整数。
- 它们的操作都是针对二进制位进行的。
举个栗子:
#include<stdio.h>
int main()
{
int a = 15;
printf("%d \n", a & 1);
printf("%d \n", a | 1);
printf("%d \n", a ^ 1);
return 0;
}
运行结果:
原理:
赋值操作符
= 赋值操作符
举个栗子:
#include<stdio.h>
int main()
{
int a = 15;
int b = 16;
printf("a = %d, b = %d \n", a, b);
a = b = 6;
printf("a = %d, b = %d \n", a, b);
return 0;
}
运行结果:
注意:
- 我们需要注意的是赋值操作符的返回值:
#include<stdio.h>
int main()
{
int a = 15;
printf("%d\n", a = 0);
printf("%d\n", a = 3);
printf("%d\n", a = 4);
printf("%d\n", a = 5);
return 0;
}
赋值操作符还包括复合操作符:
+= -= *= /= %= >>= <<= &= |= ^=
举个栗子:
#include<stdio.h>
int main()
{
int a = 15;
a += 1;
printf("a = %d \n", a);
a = 15;
a -= 1;
printf("a = %d \n", a);
a = 15;
a ^= 1;
printf("a = %d \n", a);
a = 15;
a >>= 1;
printf("a = %d \n", a);
a = 15;
a %= 2;
printf("a = %d \n", a);
return 0;
}
运行结果:
单目操作符
! 逻辑反操作
— 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
() 强制类型转换
举个栗子:
#include<stdio.h>
int main()
{
int a = 15;
printf("!a = %d\n", !a);
printf("-a = %d\n", -a);
printf("&a = %p\n", &a);
printf("sizeof(a) = %d\n", sizeof(a));
printf("~a = %d\n", ~a);
printf("--a = %d\n", --a);
printf("a = %d\n", a);
printf("a-- = %d\n", a--);
printf("a = %d\n", a);
a = 65;
int *p = &a;
printf("*p = %d\n", *p);
printf("(char *)a = %c", (char*)a);
return 0;
}
运行结果:
注意:
1. sizeof是在编译期间计算的,所以sizeof中的参数如果是语句,那么它是不执行的。
#include<stdio.h>
int main()
{
int a = 15;
printf("sizeof(a) = %d\n", sizeof(a = 6));
printf("a = %d\n", a);
return 0;
}
运行结果:
2. sizeof与数组:
#include<stdio.h>
int main()
{
int a[10] = {0};
printf("sizeof(a) = %d\n", sizeof(a)); //数组名单独放在sizeof中,大小为数组大小
printf("sizeof(&a) = %d\n", sizeof(&a)); //&a 放在sizeof中,表示的是数组的地址
printf("sizeof(a[0]) = %d\n", sizeof(a[0])); //数组首元素的字节大小
printf("sizeof(a[10]) = %d\n", sizeof(a[10])); //由于sizeof在编译阶段计算,所以这个语句是正确的,表示的是a[10]大小
printf("num = %d\n", sizeof(a) / sizeof(a[0])); // 数组总大小/数组首元素大小 表示的是数组元素个数
return 0;
}
运行结果:
3. 由于sizeof是操作符,所以sizeof还可以这样用(不推荐这样使用):
#include<stdio.h>
int main()
{
int a[10] = {0};
printf("sizeof a = %d\n",sizeof a);
return 0;
}
运行结果:
关系操作符
> >= < <= != ==
以上是常用的关系操作符。
注意:
- 在编程过程中容易将
==
与=
混淆,因此应该特别注意。
逻辑操作符
&& 逻辑与
|| 逻辑或
注意:
1. 使用过程中应注意逻辑与、逻辑或和按位与、按位或的区别。
1 & 2 -------> 0
1 | 2 -------> 3
1 && 2 -------> 1
1 || 2 -------> 1
2. 逻辑与和逻辑或的特点:
&&
特点:
#include<stdio.h>
int main()
{
int a = 0, b = 1, c = 2, d = 3;
int w = a++ && b++ && c++ && ++d;
printf("a = %d b = %d c = %d d = %d \n", a, b, c, d);
w = a++ && b++ && c++ && ++d;
printf("a = %d b = %d c = %d d = %d \n", a, b, c, d);
return 0;
}
运行结果:
逻辑与只要有一个表达式为false,就不再计算右边的表达式,直接得出false。类似于短路了右边。
||
特点:
#include<stdio.h>
int main()
{
int a = 0, b = 1, c = 2, d = 3;
int w = a++ || b++ || c++ || ++d;
printf("a = %d b = %d c = %d d = %d \n", a, b, c, d);
w = a++ || b++ || c++ || ++d;
printf("a = %d b = %d c = %d d = %d \n", a, b, c, d);
return 0;
}
运行结果:
逻辑或只要有一个表达式为true,就不计算再右边的表达式,直接得出true。类似于短路了右边。
条件操作符
x = exp1 ? exp2 : exp3
举个栗子:
#include<stdio.h>
int main()
{
int a = 1, b = 5, c = 0;
if (a > b)
c = a;
else c = b;
printf("c = %d\n", c);
c = a < b ? a : b;
printf("c = %d\n", c);
return 0;
}
运行结果:
逗号表达式
exp1, exp2, exp3, exp4, …, expn
//逗号表达式,就是用逗号隔开的多个表达式。
//逗号表达式,从左向右依次执行,逗号表达式的结果是最后一个表达式的结果。
举个栗子:
#include<stdio.h>
int main()
{
int a = 0;
while ((a++, printf("a = %d\n", a), a < 5))
{
}
return 0;
}
运行结果:
下标引用、函数调用和结构成员
[] 下标引用
() 函数调用
. 结构体.成员名
-> 结构体指针->成员名
1. [] 下标引用
操作数: 数组名 + 索引值
举个栗子:
#include<stdio.h>
int main()
{
int a[10] = {0};
printf("%d\n", a[5]);
return 0;
}
// []的两个操作数是 a 和 5
2. () 函数调用操作符
接收一个或者多个操作数: 第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
举个栗子:
#include<stdio.h>
void Print1()
{
printf("Print\n");
}
void Print2(int a)
{
printf("Print: a = %d\n", a);
}
int main()
{
int a = 6;
Print1();
Print2(a);
return 0;
}
运行结果:
3. .
结构体.结构体成员
->
结构体指针->结构体成员
举个栗子:
#include<stdio.h>
struct student
{
char name[20];
int age;
};
int main()
{
struct student A = {"zhangsan", 15};
struct student *B = &A;
printf("name : %s age : %d\n", A.name, A.age);
printf("name : %s age : %d\n", B->name, B->age);
return 0;
}
运行结果:
最后需要注意的是:
复杂操作符的求值有三个影响因素:
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
两个相邻的操作符优先执行哪个,取决于它们的优先级,如果优先级相同,则取决于它们的结合性。
下面附上操作符的优先级表:
图片来自《C和指针》
以上即为我对于常见操作符的个人理解,不足之处还望指正!