【C语言】第二期——运算符与表达式

目录

1 算术运算符

1.1 加减乘除取余 

1.2 a++ 与 ++a 的区别 

1.3 优先级

2 赋值运算符

2.1 复杂的赋值运算

3 关系运算符

4 逻辑运算符

4.1 逻辑与运算短路

4.2 逻辑或运算符

5 条件运算符(三元运算符)与条件表达式

6 特殊运算符 

6.1 求字节数运算符(sizeof)

6.2 强制类型转换运算符

7 进制(位运算符前言)

7.1 C语言进制介绍

7.2 不同进制的表示

7.2.1 二进制

7.2.2 八进制

7.2.3 十六进制

7.3 不同进制的printf占位符

8 位运算符

8.1 按位与、按位或、按位异或

8.2 按位取反 、反码、补码

8.3 移位符

9 优先级


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=30

a+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=41

b=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;
 }

运行结果:
0

1

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指令集配合使用,并且可以更好地控制内存访问等操作 。

十进制二进制十六进制十进制二进制十六进制
000000810008
100011910019
200102101010A
300113111011B
401004121100C
501015131101D
601106141110E
701117151111F

我们都知道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按位取反的十进制值是-2

b按位取反的十进制值是-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||
条件?:从右到左
赋值=  +=  -=  *=  /=  %=  >>=  <<=  &=  ^=  |=从右到左
逗号,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值