引入:
操作符是一个表示特定的数学或逻辑操作的符号,如数学中的加法,在c语言中也有+,而c语言中不止有加减乘除,通过本小节来学习并了解和学习这些操作符。
一、操作符分类
算数操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
二、详细介绍操作符
1.算数操作符
+ - * / %
加减乘和数学中的运算符用法一样,这里重点介绍 / 和 %(取模)
1)对于 / 操作符,如果两个操作数都为整数,执行整数除法;而只要有浮点数,执行的就是浮点数除法。这句话理解如下:
int main()
{
int a = 10 / 3;
double b = 10.0 / 3;
double c = 10 / 3.0;
printf("a=%d\n", a); //结果为3,因为10和3都是int型
printf("b=%lf\n", b); //结果为3.333333,因为10.0是double型
printf("a=%lf\n", c); //结果为3.333333,因为3.0是double型
return 0;
}
2)% 的意思是求余,如10%3的结果为1,因为10除以3的余数是1。
%操作符的两个操作数必须为整数,如10.0%3这样的写法就是错误的。%返回的是整除后的余数
2.移位操作符
1)引入:在介绍移位操作符之前,我们先介绍整数在内存中如何存储的
整数有三种二进制表示形式:原码、补码、反码。
整数在内存中存储的是补码
正数的原码、反码、补码
如 int a = 5,a是整型,占内存的4个字节,即32bit。5用二进制表示是101,因为占32个bit,所以将它补全即为 00000000 00000000 00000000 00000101 ,这个就是5的原码
正数的原码、反码、补码都是这个
负数的原码、反码、补码
如 int a = -5,
5的原码是00000000 00000000 00000000 00000101
-5的原码就是将它的第一位0变成1,即10000000 00000000 00000000 00000101
-5的反码就是将原码的符号位不变,就是第一位不变,其余的0变成1,1变成0
即11111111 11111111 11111111 11111010
-5的补码就是反码的二进制+1
即11111111 111111111 11111111 11111011
2) 左移操作符
规则:左边抛弃,右边补0
对于正数的左移操作,因为正数的原、反、补码都一样,所以直接将原码左移就可以了。
int main()
{
int a = 5;
int b = a << 2;//把a在内存中存储的二进制位向左移动两位
printf("a=%d b=%d\n",a, b);//a=5,b=20
return 0;
}
下面画图进行解释,二进制位向左移动两位。
对于负数的左移操作,需要求出其补码,将补码移位,再将移位后的补码转化为原码,求出其值。因为打印或使用的时候,用的是原码的值。
int a = -5;
int b = a<<2;
11111111 111111111 11111111 11111011 -5的补码
11111111 111111111 11111111 11101100 将-5的补码左移两位
11111111 111111111 11111111 11101011 反码:补码减一
10000000 00000000 00000000 00010100 原码:结果是-20
3)右移操作符
规则:算数右移:右边丢弃,左边补原来的符号位
逻辑右移:右边丢弃,左边补0
到底是算数右移还是逻辑右移,取决于编译器,我们常见的编译器都是算数右移
对于正数的右移,直接将原码右移,补符号位时补0
int main()
{
int a = 5;
int b = a >> 1;
printf("%d\n", b);//结果是2
return 0;
}
对于负数的右移和左移相似,也是将其补码右移,唯一区别的就是左边补位的时候,不是补0,而是补符号位1
3.位操作符
1)& 按位与:按(2进制)位与-----两个数的补码对应位只要有0就是0,两个同时为1才为1
int main()
{
int a = 3;
int b = -5;
int c = a & b;
printf("%d\n", c);//结果是3
return 0;
}
解释:00000000 00000000 00000000 00000011 3的补码
11111111 11111111 11111111 11111011 -5的补码
00000000 00000000 00000000 00000011 按位与后的补码
因为符号位是0,所以这是一个正数的补码,原码和补码相同,所以结果是3
2)| 按位或:按(2进制)位或---两个数的补码对应位只要有1就是1,两个同时为0才为0
int main()
{
int a = 3;
int b = -5;
int c = a | b;
printf("%d\n", c);//结果是-5
return 0;
}
解释: 00000000 00000000 00000000 00000011 3的补码
11111111 11111111 11111111 11111011 -5的补码
11111111 11111111 11111111 11111011 按位或后的补码,符号位是1,所以是个负数,需要求它的原码
11111111 11111111 11111111 11111010 按位或后的反码
10000000 00000000 00000000 00000101 按位或后的原码,即为-5
3)^ 按位异或:按(2进制)位异或:两个对应位相同为0,相异为1
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;
printf("%d\n", c); //结果是-8
return 0;
}
解释:00000000 00000000 00000000 00000011 3的补码
11111111 11111111 11111111 11111011 -5的补码
11111111 11111111 11111111 11111000 按位异或后的补码,这是个负数,要求出它的原码
11111111 11111111 11111111 11110111 按位异或后的反码
10000000 00000000 00000000 00001000 按位异或后的原码,即为-8
4.赋值操作符
= 用来给变量赋值,如 int a = 10;
符合赋值操作符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
复合操作符的使用用法如下:
这里用‘+=’为例
int main()
{
int a = 0;
a += 10; //等价于a = a+10
printf("a=%d\n",a);//结果为10
return 0;
}
5.单目操作符
1)! 逻辑反操作 :作用是把假的变成真的,把真的变成假的
int main()
{
int flag = 0;
if (!flag) //因为flag为0是假,!flag为真,进入if语句
{
printf("我爱c语言\n");
}
return 0; //打印我爱c语言
}
2)- 负值
例:在屏幕上打印 0 -1 2 -3 4 -5 6 -7 8 -9
int main()
{
int i = 0;
int flag = 1;
for (i = 0; i < 10; i++)
{
printf("%d ", i * flag);
flag = -flag;
}
return 0;
}
3)+ 正值
4)& 取地址:拿取在内存中的地址
5)sizeof 操作数的类型长度(以字节为单位)
int main()
{
int a = 10;
printf("%d\n", sizeof(a));//结果是4,因为int是整型,整型占四个字节
printf("%d\n", sizeof(int));//结果是4
int arr[10] = { 1,2,3,4,5,6 };
printf("%d\n", sizeof(arr)); //结果是40 计算整个数组的大小
printf("%d\n", sizeof(int [10]));//结果是40,数组里有10个int型,所以是40
return 0;
}
6)~ 对一个数的二进制按位取反
int main()
{
int a = 0;
//00000000 00000000 00000000 00000000 这是0的补码
//11111111 11111111 11111111 11111111 这是按位取反后的结果,这个依然是补码
//11111111 11111111 11111111 11111110 这是反码
//10000000 00000000 00000000 00000000 这是原码,是~a最后的结果,即是-1
printf("%d\n", ~a);//结果是4,因为int是整型,整型占四个字节
return 0;
}
7)++ 前置、后置++
int main()
{
int a = 10;
int b = ++a; //前置++,先++,后使用。即先把a+1,再赋值给b
int c = 10;
int d = c++; //后置++,先使用,后++。即先把c的值赋给d,c再+1
printf("a=%d b=%d\n",a, b); //a = 11 b = 11
printf("c=%d d=%d\n",c, d);//c = 11 d = 10
return 0;
}
8)-- 前置、后置-- :和上面相似,就不多做介绍了。
9)* 间接访问操作符(解引用操作符):对地址进行解引用操作
int main()
{
int a = 0;
int* pa = &a; //pa里面放的是a的地址,*pa找到a的空间
*pa=20; //找到a的空间,将a的值改为20
printf("%d\n", a); //打印20
return 0;
}
10)(类型) 强制类型转换
int main()
{
//int a = 3.14;//3.14是double类型的数据,把它放在int型里会丢失数据,并且会报警告
int a = (int)3.14;//强制类型转换,将3.14强制转换成int型
printf("%d\n", a);//结果是3,不会报警告
return 0;
}
强制类型转化不建议经常使用。
6.关系操作符
1) >
2) >=
3) <
4) <=
5) !=
6) ==
7.逻辑操作符
逻辑操作符只关注真假
1) && 逻辑与:表示并且
例:写一个代码,当从键盘输入一个大于0小于18的数时打印未成年
int main()
{
int age = 0;
//if (0 < age < 18)//这种写法是错误的。例如输入20,而20是大于0的,是真,也会进入if中
//{
// printf("未成年");
//}
scanf("%d", &age);
if (age > 0 && age < 18)
{
printf("未成年");
}
return 0;
}
2)|| 逻辑或:表示或者
例:几个练习
&& 左操作数为假,右边不计算。为真的时候才计算右边
|| 左操作数为真,右边不计算。为假的时候才计算右边
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;//a=0是假,后面就不需要计算了,b、d的值保持不变
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//a,b,c,d的值分别为1,2,3,4
return 0;
}
int main()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;//a++是真,右边的++b,d++计算
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//a,b,c,d的值为2,3,3,5
return 0;
}
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;//因为++b是真,右边的d++不计算
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//a,b,c,d的结果是1,3,3,4
return 0;
}
8.条件操作符(三目操作符)
表达式1 ? 表达式2 : 表达式3:表达式1结果为真,就执行表达式2;为假就执行表达式3
例:利用三目操作符来判断两个数的较大值
int main()
{
int a = 10;
int b = 20;
int max = 0;
max = (a > b ? a : b);
printf("%d\n", max);
return 0;
}
9.逗号表达式
表达式1,表达式2,..... ,表达式n
逗号表达式就是用逗号隔开的多个表达式。
逗号表达式从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int main()
{
int a = 3;
int b = 5;
int c = 6;
int d = (a += 2, b = a - c, c = a + 2 * b);
printf("%d\n", d);//结果是3
return 0;
}
10.下标引用 、函数调用和结构成员
1) [ ] 下标引用操作符
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", arr[6]);//操作数是arr和6,结果是7
printf("%d\n", 6[arr]);//arr[7] ——->*(arr+6)-->*(6+arr)-->6[arr],结果也是7
return 0;
}
2)函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
int Add(int x, int y)
{
return x + y;
}
int main()
{
int ret = Add(2, 3);
printf("%d\n", ret);
return 0;
}
3.访问一个结构体成员
. 结构体变量.结构体成员
-> 结构体指针->结构体成员
struct Stu
{
char name[20];
int age;
double score;
};
int main()
{
// .
struct Stu s = {"xiaohong", 19, 95.5};
printf("%s %d %.1lf\n", s.name, s.age, s.score);
// ->
struct Stu* ps = &s;
printf("%s %d %.1lf\n", (*ps).name, (*ps).age, (*ps).score);
printf("%s %d %.1lf\n", ps->name, ps->age, ps->score);
return 0;
}