目录
C语言常用的运算符: 算术运算符 赋值运算符 关系运算符 逻辑运算符 条件运算符 类型转换运算符 求字节数运算符 位运算符 ...
1 算术运算符
运算符 | 描述 |
---|---|
+ | 把两个操作数相加 |
- | 从第一个操作数中减去第二个操作数 |
* | 把两个操作数相乘 |
/ | 分子除以分母 |
% | 取模运算符,整除后的余数 |
++ | 自增运算符,整数值增加 1 |
-- | 自减运算符,整数值减少 1 |
1.1 加减乘除取余
//加
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("a+b=%d\n", a + b); // 变量相加
printf("a+1=%d\n", a + 1); // 变量与常量相加
printf("1+1=%d\n", 1 + 1); // 常量与常量相加
return 0;
}
运行结果:
a+b=30a+1=11
1+1=2
//减
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("b-a=%d\n", b - a); // 变量相减
printf("a-1=%d\n", a - 1); // 变量与常量相减
printf("3-1=%d\n", 3 - 1); // 常量与常量相减
return 0;
}
运行结果:
b-a=10
a-1=9
3-1=2
//乘
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("b*a=%d\n", b * a); // 变量相乘
printf("a*1=%d\n", a * 1); // 变量与常量相乘
printf("3*1=%d\n", 3 * 1); // 常量与常量相乘
return 0;
}
运行结果:
b*a=200
a*1=10
3*1=3
//除
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("b/a =%d\n", b / a);
printf("a/1 =%d\n", a / 1);
printf("20/10=%d\n", 20 / 10);
printf("b/6 =%d\n", b / 6);
printf("b/6.0 =%lf\n", b / 6.0);
printf("20/6 =%d\n", 20 / 6);
printf("20/6.0=%lf\n", 20 / 6.0);
return 0;
}
运行结果:
b/a =2
a/1 =10
20/10=2
b/6 =3
b/6.0 =3.333333
20/6 =3
20/6.0=3.333333
//取余
#include <stdio.h>
int main()
{
int a = 6;
int b = 20;
printf("%d\n", b % a);
printf("%d\n", a % 5);
printf("%d\n", 20 % 3);
printf("%d\n", 20 % 4);
return 0;
}
运行结果:
2
1
2
0
1.2 a++ 与 ++a 的区别
#include <stdio.h>
int main()
{
printf("先赋值后运算:\n");
int c;
int a = 10;
c = a++;
printf("Line 1 - c 的值是 %d\n", c );
printf("Line 2 - a 的值是 %d\n", a );
a = 10;
c = a--;
printf("Line 3 - c 的值是 %d\n", c );
printf("Line 4 - a 的值是 %d\n", a );
//——————————————————————————————————————————————————————————————
printf("先运算后赋值:\n");
a = 10;
c = ++a;
printf("Line 5 - c 的值是 %d\n", c );
printf("Line 6 - a 的值是 %d\n", a );
a = 10;
c = --a;
printf("Line 7 - c 的值是 %d\n", c );
printf("Line 8 - a 的值是 %d\n", a );
}
运行结果:
先赋值后运算:Line 1 - c 的值是 10
Line 2 - a 的值是 11
Line 3 - c 的值是 10
Line 4 - a 的值是 9
先运算后赋值:
Line 5 - c 的值是 11
Line 6 - a 的值是 11
Line 7 - c 的值是 9
Line 8 - a 的值是 9
1.3 优先级
当算术表达式由多个不同的算术运算符组成时,会按照运算符的优先级进行运算:
先乘除后加减、先括号里再括号外,
优先级相同,按照自左向右的顺序进行运算
分析:a*10+(100%3)-b/10 的值
#include <stdio.h>
int main()
{
int a = 12;
int b = 100;
printf("%d", a * 10 + (100 % 3) - b / 10);
}
2 赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A |
%= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A |
可以运行一下以下代码:
#include<stdio.h>
int main()
{
int a;
int b;
int c;
a = 20; // 将数值 20 赋值给变量 a
b = a - 10; // 先计算 a-10,然后将计算结果赋值给变量 b
c = a + b; // 先计算 a+b,然后将计算结果赋值给变量 c
printf("a=%d\n", a);
printf("b=%d\n", b);
printf("c=%d\n", c);
return 0;
}
#include <stdio.h>
int main()
{
int a = 21;
int c;
c = a;
printf("Line 1 - = 运算符实例,c 的值 = %d\n", c);
c += a;
printf("Line 2 - += 运算符实例,c 的值 = %d\n", c);
c -= a;
printf("Line 3 - -= 运算符实例,c 的值 = %d\n", c);
c *= a;
printf("Line 4 - *= 运算符实例,c 的值 = %d\n", c);
c /= a;
printf("Line 5 - /= 运算符实例,c 的值 = %d\n", c);
c = 200;
c %= a;
printf("Line 6 - %%= 运算符实例,c 的值 = %d\n", c);
}
2.1 复杂的赋值运算
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
a += b * 20;
b %= a + 10;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
运行结果:
a=41b=2
3 关系运算符
关系运行符中输出1表示真,输出0表示假。
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 为假 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真 |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A> B) 为假 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A>= B) 为假 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真 |
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
printf("%d\n", 1 > 2);
printf("%d\n", 5 >= 2);
printf("%d\n", a < 2);
printf("%d\n", a <= b);
printf("%d\n", a + b == c);
printf("%d\n", c != a + b);
return 0;
}
运行结果:
01
0
1
1
0
4 逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 | (A && B) 为假 |
|| | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 | (A || B) 为真 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 | !(A && B) 为真 |
#include<stdio.h>
int main()
{
int gender = 1; // gender 性别: 1 表示男, 0 表示女
int adult = 1; // adult 是否成年: 1 表示成年, 0 表示未成年
printf("%d\n", gender && adult);
printf("%d\n", gender || adult);
printf("%d\n", !gender);
printf("%d\n", !adult);
getchar();
return 0;
}
#include <stdio.h>
int main()
{
int a = 5;
int b = 20;
printf("%d\n", a && b);
printf("%d\n", a || b);
printf("%d\n", !a);
}
4.1 逻辑与运算短路
当“&&”左侧为“假”时,逻辑与运算表达式结果直接为“假”,“&&”右侧将不再进行判断
#include <stdio.h>
int main()
{
int a = 8;
int b = (a > 5) && ((a = a + 5) < 10);
printf("b=%d\n", b);
printf("a=%d", a);
return 0;
}
运行结果:
b=0
a=13
#include <stdio.h>
int main()
{
int a = 8;
int b = (a < 5) && ((a = a + 5) < 10);
printf("b=%d\n", b);
printf("a=%d", a);
return 0;
}
运行结果:
b=0
a=8
4.2 逻辑或运算符
当“||”左侧为“真”时,逻辑或运算表达式结果直接为“真”,“||”右侧将不再进行判断
#include <stdio.h>
int main()
{
int a = 8;
int b = (a < 5) || ((a = a + 5) < 10);
printf("b=%d\n", b);
printf("a=%d", a);
return 0;
}
运行结果:
b=0
a=13
#include <stdio.h>
int main()
{
int a = 8;
int b = (a > 5) || ((a = a + 5) < 10);
printf("b=%d\n", b);
printf("a=%d", a);
return 0;
}
运行结果:
b=1
a=8
5 条件运算符(三元运算符)与条件表达式
条件运算符又被称为三元运算符,是C语言中唯一的一个三元运算符,基本格式如下:
表达式1 ? 表达式2 : 表达式3
max = a>b ? a : b
其求值规则为:如果表达式1的值为真,则以表达式2 的值作为整个条件表达式的值,否则以表达式3的 值作为整个条件表达式的值。条件表达式通常用于赋值语句之中。
#include <stdio.h>
int main()
{
int a = 9;
nt b = 0;
b = (a > 10 ? 888 : 666);
printf("b=%d\n", b);
b = (a > 5 ? 888 : 666);
printf("b=%d\n", b);
return 0;
}
6 特殊运算符
6.1 求字节数运算符(sizeof)
c语言中,使用sizeof运算符可以获取一个数据类型或者一组数据类型的字节数。
例如,sizeof(int)会返回4,因为int类型通常占据4个字节。
#include<stdio.h>
int main()
{
printf("sizeof(char) =%d\n", sizeof(char));
printf("sizeof(int) =%d\n", sizeof(int));
printf("sizeof(float) =%d\n", sizeof(float));
printf("sizeof(double)=%d\n", sizeof(double));
return 0;
}
运行结果:
sizeof(char) =1
sizeof(int) =4
sizeof(float) =4
sizeof(double)=8
6.2 强制类型转换运算符
强制类型转换运算符由括号“( )“和数据类型两部分构成,形为:(数据类型)
其一般使用形式为:
(数据类型)常量;
(数据类型)变量;
(数据类型)(表达式);
其作用是将常量、变量、表达式运算结果等,转换为括号中的指定数据类型
#include <stdio.h>
int main()
{
int a;
float f;
a = (int)3.14;
f = (float)(10 + 20);
printf("a=%d\n", a);
printf("f=%f\n", f);
return 0;
}
运行结果:
a=3
f=30.000000
7 进制(位运算符前言)
位运算符通常用于进行二进制操作
在了解位运算符前我们首先需要知道什么是10进制、2进制 、16进制
7.1 C语言进制介绍
对于计算机来说,本质上只能识别和执行0和1组成的二进制指令,例如:
0101 1111 0000 0000
在嵌入式系统开发中,16进制和二进制通常用于将数据转换为易于处理的形式。
例如,在嵌入式系统中使用16进制可以更容易地处理数据和变量,因为大多数硬件都支持16进制。
同样,二进制是嵌入式系统中最常用的编码方式之一,因为它可以轻松地与CPU指令集配合使用,并且可以更好地控制内存访问等操作 。
十进制 | 二进制 | 十六进制 | 十进制 | 二进制 | 十六进制 |
---|---|---|---|---|---|
0 | 0000 | 0 | 8 | 1000 | 8 |
1 | 0001 | 1 | 9 | 1001 | 9 |
2 | 0010 | 2 | 10 | 1010 | A |
3 | 0011 | 3 | 11 | 1011 | B |
4 | 0100 | 4 | 12 | 1100 | C |
5 | 0101 | 5 | 13 | 1101 | D |
6 | 0110 | 6 | 14 | 1110 | E |
7 | 0111 | 7 | 15 | 1111 | F |
我们都知道10进制逢10进1
同理:
二进制是逢2进1,一般我们用四位为一组表示一个二进制数
16进制是逢16进1,一般嵌入式中我们用一个16进制数表示一组二进制数
7.2 不同进制的表示
7.2.1 二进制
二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头
例如:
//合法的二进制
int a = 0b101; //换算成十进制为 5
int b = -0b110010; //换算成十进制为 -50
int c = 0B100001; //换算成十进制为 33
//非法的二进制
int m = 101010; //无前缀 0B,相当于十进制
int n = 0B410; //4不是有效的二进制数字
注意:标准的C语言并不支持上面的二进制写法,只是有些编译器自己进行了扩展,才支持二进制数字。换句话说,并不是所有的编译器都支持二进制数字,只有一部分编译器支持,并且跟编译器的版本有关系。
7.2.2 八进制
八进制由 0~7 八个数字组成,使用时必须以0开头
//合法的八进制数
int a = 015; //换算成十进制为 13
int b = -0101; //换算成十进制为 -65
int c = 0177777; //换算成十进制为 65535
//非法的八进制
int m = 256; //无前缀 0,相当于十进制
int n = 03A2; //A不是有效的八进制数字
7.2.3 十六进制
十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,使用时必须以 0x 或 0X(不区分大小写)开头
例如:
//合法的十六进
int a = 0X2A; //换算成十进制为 42
int b = -0XA0; //换算成十进制为 -160
int c = 0xffff; //换算成十进制为 65535
//非法的十六进制
int m = 5A; //没有前缀 0X,是一个无效数字
int n = 0X3H; //H不是有效的十六进制数字
7.3 不同进制的printf占位符
%d 可以以十进制输出整数、%o可以以八进制输出整数、%X可以以16进制输出整数
%x
和%X
这两个占位符中x
的大小写会决定输出十六进制数时字母部分的大小写
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 12;
printf("八进制 --> %o\n", a);
printf("十六进制 --> %X\n", a);
printf("十进制 --> %d\n", a);
char s[16];
itoa(a, s, 2);
printf("二进制 --> %s\n", s);
return 0;
}
8 位运算符
运算符 | 描述 |
---|---|
& | 按位与:对两个操作数的每一位执行逻辑与操作,如果两个相应的位都为 1,则结果为 1,否则为 0。按位与操作,按二进制位进行 “与” 运算。运算规则:0 & 0 = 0; 0 & 1 = 0; 1 & 0 = 0; 1 & 1 = 1; |
| | 按位或:对两个操作数的每一位执行逻辑或操作,如果两个相应的位都为 0,则结果为 0,否则为 1。按位或运算符,按二进制位进行 “或” 运算。运算规则:0 | 0 = 0; 0 | 1 = 1; 1 | 0 = 1;1 | 1 = 1; |
^ | 按位异或:对两个操作数的每一位执行逻辑异或操作,如果两个相应的位值相同,则结果为 0,否则为 1。异或运算符,按二进制位进行 “异或” 运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; |
~ | 按位取反:对操作数的每一位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0。取反运算符,按二进制位进行 “取反” 运算。运算规则:~1=-2; ~0=-1;(详看补码系统) |
<< | 按位左移:将操作数的所有位向左移动指定的位数。左移 n 位相当于乘以 2 的 n 次方。二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补 0)。 |
>> | 按位右移:将操作数的所有位向右移动指定的位数。右移 n 位相当于除以 2 的 n 次方。二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补 0,负数左补 1,右边丢弃。 |
8.1 按位与、按位或、按位异或
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a = 0b00000001; // 相当于10进制1
char b = 0b00001001; // 相当于10进制9
printf("a对应的10进制%d\n", a);
printf("b对应的10进制%d\n", b);
// 00000001 & 00001001=00000001 如果两个相应的位都为 1,则结果为 1
printf("a&b 按位与值%d\n", a & b);
// 00000001 & 00001001=00001001 如果两个相应的位都为 0,则结果为 0
printf("a|b 按位或的值%d\n", a | b);
// 00000001 ^ 00001001=00001000 如果两个相应的位值相同,则结果为 0
printf("a^b 按位异或的值%d\n", a ^ b);
}
8.2 按位取反 、反码、补码
对操作数的每一位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0
如 0 000 0001 按照位取反后得到的二进制位 1111 1110
如 0 000 1001 按照位取反后得到的二进制位 1111 0110
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a = 0b00000001; // 相当于10进制1
printf("a按位取反的十进制值是%d\n", ~a);
char b = 0b00001001; // 相当于10进制9
printf("b按位取反的十进制值是%d", ~b);
}
输出结果为:
a按位取反的十进制值是-2b按位取反的十进制值是-10
问题: 这个结果和我们上面想要的结果出入很大,为什么呢?
原因:在计算机中数据是以其补码形式存储和运算的 。
首先补充几个概念:
机器数:数值在计算机中的二进制表示形式,机器数通常使用最高位作为符号位,一般规定
0
表示正数,1
表示负数真值:一个数在数学中的实际数值
原码:一种最简单的机器数表示法。最高位为符号位,其余位表示数值的绝对值。符号位用
0
表示正数,用1
表示负数。(可进行无符号计算)反码:反码的符号位与原码相同,正数的反码和原码相同;负数的反码是在原码的基础上,符号位不变,其余各位按位取反。(可参与符号计算,但会出现-0的情况,即11111111)
补码:正数的补码和原码、反码相同;负数的补码是在其反码的基础上加
1
。补码解决了原码在加减法运算中的问题,使得计算机可以使用加法器统一进行加法和减法运算。(可参与符号计算,排除了-0的情况)
在上文代码中,a变量存储的值0000 0001首位为0,为正数,所以其反码补码都是0000 0001
我们提到,在计算机中数据是以补码形式参与计算的,所以在其参与计算(取反计算)后,得到的数还是补码 1111 1110 ,
最终我们是让其以十进制打印出结果,即打印真值,而此时还是补码,因此我们要将补码转成原码(因为原码可以直观转化为真值)
操作为原码转补码的逆运算:
第一步:1111 1110先减一,得到1111 1101(此时为反码)
第二步:再将其除了符号位都取反,即 1000 0010(此时为原码)
第三步:原码即可转化为真值
1000 0010,首位为1,为负数,剩下七位转成十进制为2,所以最终显示-2
注意:补码先加一再取反效果与上述一样,但不便于理解,不推荐
视频教程:
【一听就懂】C语言必会之原码/反码/补码!分析+实例,十分钟带你彻底掌握,这可比学校讲的细致多了!_哔哩哔哩_bilibili
8.3 移位符
<< 按位左移:
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
>> 按位右移:
将一个数的各二进制位全部右移若干位
注意所移动的值最高位是否表示符号,以及移动过后正负性的改变
#include <stdio.h>
int main()
{
char a = 0b00001000;
// 0000 1000 左移2位0010 0000
printf("a按位左移2位后的的十进制值是%d\n", a << 2);
// 0000 1000 右移动2位 0000 0010
}
9 优先级
下表将按运算符优先级从高到低列出各个运算符
(具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面)
在表达式中,较高优先级的运算符会优先被计算
表格里的 “结合性” 指的是当一个表达式中有多个相同的运算符时,运算执行的方向
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] ->. ++ -- | 从左到右 |
一元 | + -! ~ ++ -- (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 无 |
位异或 XOR | ^ | 无 |
位或 OR | ** | 无 |
逻辑与 AND | && | 无 |
逻辑或 OR | || | 无 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %= >>= <<= &= ^= |= | 从右到左 |
逗号 | , | 无 |