C语言中的操作符:了解与实践

时间:2024-01-22 20:08:28

pFp8UCq.jpg

​ ✨✨ 欢迎大家来到贝蒂大讲堂✨✨

​ ????????养成好习惯,先赞后看哦~????????

​ 所属专栏:C语言学习

​ 贝蒂的主页:Betty‘s blog

1. 操作符的分类

操作符又叫运算符,它在C语言中起着非常大的作用,以下是对操作符的分类:

  1. 算术操作符: + 、- 、* 、/ 、%
  2. 移位操作符: <<、 >>
  3. 位操作符: & 、| 、^、~
  4. 赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
  5. 单目操作符:!、++、--、&、*、+、-、~ 、sizeof、(类型)
  6. 关系操作符:> 、>= 、< 、<= 、 == 、 !=
  7. 逻辑操作符: && 、||
  8. 条件操作符: ? :
  9. 逗号表达式: ,
  10. 下标引用:[]
  11. 函数调用: ()
  12. 结构成员访问: . 、->

2. 算术操作符

2.1 用法

算术操作符顾名思义就是参与运算的操作符

下表显示了 C 语言支持的所有算术操作符。假设变量 A 的值为 1,变量 B 的值为 2,则:

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 3
- 从第一个操作数中减去第二个操作数 A - B 将得到 -1
* 把两个操作数相乘 A * B 将得到 2
/ 分子除以分母 A / B 将得到 0
% 取模运算符,整除后的余数 A%B将得到 1

2.2 代码实现

#include<stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c;
	c = a + b;
	printf("a+b=%d\n", c);
	c = a - b;
	printf("a-b=%d\n", c);
	c = a * b;
	printf("a*b=%d\n", c);
	c = a / b;
	printf("a/b=%d\n", c);
	c = a % b;
	printf("a%%b=%d\n", c);
	return 0;

输出结果: a+b=3 a-b=-1 a*b=2 a/b=0 a%b=1

3. 移位操作符

3.1 用法

移位操作符改变的是二进制序列,所以操作对象是整数,且移动位数不能为负数

下表显示了 C 语言支持的移位操作符。假设变量 A 的值为 1:

运算符 描述 实例
<< 将操作数的所有位向左移动指定的位数。运算规则:左边的二进制位丢弃,右边补0。 A<<1=2
>> 将操作数的所有位向右移动指定的位数。运算规则分两种:1. 逻辑右移:左边⽤0填充,右边丢弃 2. 算术右移:左边⽤原该值的符号位填充,右边丢弃 A>>1=0

3.2 图像演示

  • 左移操作符

  • 右移操作符

3.3 代码实现

int main()
{
	int  a = 1;
	printf("左移之后值为%d\n", a << 1);
	int b = -1;
	printf("右移之后值为%d\n",b>> 1);
	return 0;
}

输出结果:

左移之后值为2 右移之后值为-1

  • 右移之后为-1,说明在VS2022的环境下,右移操作符是算术右移

4. 位操作符

4.1 用法

位操作符与移位操作符一样作用对象是二进制序列,所以操作数自然只能为整数

下表显示了 C 语言支持的位操作符。假设变量 A 的值为 1,变量 B 的值为 2,则:

运算符 描述 实例
& 对两个操作数的每个二进制位执行逻辑与操作,如果两个相应的位都为 1,则结果为 1,否则为 0。 (A & B) 将得到 0
| 对两个操作数的每个二进制位执行逻辑或操作,如果两个相应的位都为 0,则结果为 0,否则为 1。 (A | B) 将得到 3
^ 对两个操作数的每个二进制位执行逻辑异或操作,如果两个相应的位值相同,则结果为 0,否则为 1。 (A ^ B) 将得到 3
~ 对操作数的每个二进制位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0。 (~A ) 将得到 -2

4.2 代码实现

int main()
{
	int a = 1;
	int b = 2;
	int c;
	c = a & b;
	printf("a&b=%d\n", c);
	c = a | b;
	printf("a|b=%d\n", c);
	c = a ^ b;
	printf("a^b=%d\n", c);
	c = ~a;
	printf("~a=%d\n", c);
	return 0;
}

输出结果:

a&b=0 a|b=3 a^b=3 ~a=-2

4.3 具体分析

  • a的原码,反码,补码:00000000000000000000000000000001
  • b的原码,反码,补码:00000000000000000000000000000010
  • a&b:00000000000000000000000000000000——>0
  • a|b:00000000000000000000000000000011——>3
  • a^b:00000000000000000000000000000011——>3
  • ~a的原码:11111111111111111111111111111110
  • ~a的反码:11111111111111111111111111111101
  • ~a的补码:10000000000000000000000000000010——>-2

4.4 有趣的例题

题目: 不能创建临时变量(第三个变量),实现两个数的交换。

代码实现:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

分析:要想解决这个问题就要使用我们刚学的异或操作符,并且我们得知道一个数异或本身是0,因为所有二进制位都相同。并且0异或任何数都等于该数,因为所有二进制位都不同。而且异或是遵循交换率结合率的。

  • 因为a=a^b,所以b=a^b=a^b^b=a^0=a
  • a=a^b=a^b^a=b^0=b,成功实现两个数的交换

5. 赋值操作符

5.1 用法

赋值操作符顾名思义,就是对变量进行赋值

下表列出了 C 语言支持的赋值操作符:

运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 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
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|= 按位或且赋值运算符 C |= 2 等同于 C = C | 2

5.2 代码实现

#include <stdio.h>
int main()
{
	int a = 10;
	int c;
	c = a;
	printf(" = 运算符实例:c 的值 = %d\n", c);
	c += a;
	printf("+= 运算符实例:c 的值 = %d\n", c);
	c -= a;
	printf("-= 运算符实例:c 的值 = %d\n", c);
	c *= a;
	printf("*= 运算符实例:c 的值 = %d\n", c);
	c /= a;
	printf("/= 运算符实例:c 的值 = %d\n", c);
	c = 200;
	c %= a;
	printf("%%= 运算符实例:c 的值 = %d\n", c);
	c <<= 2;
	printf("<<= 运算符实例:c 的值 = %d\n", c);
	c >>= 2;
	printf(">>= 运算符实例:c 的值 = %d\n", c);
	c &= 2;
	printf("&= 运算符实例:c 的值 = %d\n", c);
	c ^= 2;
	printf("^= 运算符实例:c 的值 = %d\n", c);
	c |= 2;
	printf("|= 运算符实例:c 的值 = %d\n", c);
	return 0;
}

输出结果:

= 运算符实例:c 的值 = 10 += 运算符实例:c 的值 = 20 -= 运算符实例:c 的值 = 10 *= 运算符实例:c 的值 = 100 /= 运算符实例:c 的值 = 10 %= 运算符实例:c 的值 = 0 <<= 运算符实例:c 的值 = 0 >>= 运算符实例:c 的值 = 0 &= 运算符实例:c 的值 = 0 ^= 运算符实例:c 的值 = 2 |= 运算符实例:c 的值 = 2

6. 单目操作符

6.1 用法

单目操作符简单来说就是,操作的对象只有一个

下表列出了 C 语言支持的单目操作符:

运算符 描述 实例
sizeof() 返回变量的大小。 sizeof(a) 将返回a变量的大小
& 返回变量的地址。 &a, 将给出变量的实际地址。
* 指向一个变量。 *a,将指向一个变量。
++ 自增运算符,整数值增加 1 a++等价于a=a+1
-- 自减运算符,整数值减少 1 a--等价于a=a-1
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真,则逻辑非运算符将使其为假。 假设a为真,!a为假
(类型) 将一种类型强制转换为另一种类型 假设a为int型,(float)a将a转换为float型

6.2 代码实现

int main()
{
	int a = 1;
	printf("a的大小为%d\n", sizeof(a));
	int* p = &a;
	printf("a的地址为%p\n",p );
	int b = *p;
	printf("b的值为%d\n", b);
	a++;
	printf("a的值为%d\n",a);
	a--;
	printf("a的值为%d\n", a);
	printf("a的值为%d\n", !a);//0为假
	float c = (float)a;
	return 0;
}

输出结果:

a的大小为4 a的地址为010FFC08 b的值为1 a的值为2 a的值为1 a的值为0

6.3 前置与后置++,--的区别

首先我们先来看一段代码

int main()
{
	int a = 1;
	int b = a++;//后置++
	printf("a=%d b=%d\n", a, b);
	int c = ++a;//前置++
	printf("a=%d c=%d\n", a, c);
	int m = a--;//后置
	printf("a=%d m=%d\n", a, m);
	int n = --a;//前置--
	printf("a=%d n=%d\n", a, n);
	return 0;
}

输出结果: a=2 b=1 a=3 c=3 a=2 m=3 a=1 n=1

通过上述代码,我们可以总结以下结论:

  1. 前置++,--先执行++或--,然后对等式左边进行赋值
  2. 后置++,--恰好相反,先对等式左边进行赋值,然后再++或--

6.4 易错题

题目:下例代码输出结果为?

int main()
{
	int a = 12;
	int b = 1;
	int c = a - (b--);//1
	int d = (++a) - (--b);//2
	printf("c=%d d=%d\n", c, d);
	return 0;
}

输出结果:

c=11 d=14

代码分析:

  1. 执行语句1时,因为b后置--,所以b先使用,后--,然后进行a-b运算,结果是 11,随后b 再自减,就变成了 0;最后再将a-b的结果(也就是11)交给 c,所以 c 的值是 11。

  2. 执行语句2之前,b 的值已经变成 0。对于d=(++a)-(--b),a 会先自增,变成 13,然后 b 再自减,变成 -1,最后再计算13-(-1),结果是 14,交给 d,所以 d 最终是 14。

7. 关系操作符

7.1 用法

关系操作符就是进行关系之间的比较

下表显示了 C 语言支持的所有关系运算符。假设变量 A 的值为 1,变量 B 的值为 2,则:

运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。

7.2 代码实现

int main()
{
	int a = 1;
	int b = 2;
    if (a == b)
    {
        printf(" a 等于 b\n");
    }
    else
    {
        printf(" 不等于 b\n");
    }
    if (a < b)
    {
        printf(" a 小于 b\n");
    }
    else
    {
        printf(" a 不小于 b\n");
    }
    if (a > b)
    {
        printf(" a 大于 b\n");
    }
    else
    {
        printf(" a 不大于 b\n");
    }
    if (a <= b)
    {
        printf(" a 小于或等于 b\n");
    }
    if (b >= a)
    {
        printf(" b 大于或等于 a\n");
    }
}

输出结果:

不等于 b a 小于 b a 不大于 b a 小于或等于 b b 大于或等于 a

8. 逻辑操作符

8.1 用法

下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。
|| 称为逻辑或运算符。如果两个操作数中有任意一个数非零,则条件为真。 (A || B) 为真。

8.2 代码实现

int main()
{
    int a = 1;
    int b = 0;

    if (a && b)
    {
        printf("a&&b条件为真\n");
    }
    else
    {
        printf("a&&b条件为假\n");
    }
    if (a || b)
    {
        printf("a||b条件为真\n");
    }
    else
    {
        printf("a||b条件为假\n");
    }
}

输出结果:

a&&b条件为假 a||b条件为真

8.3 补充讲解

  1. 下列代码会输出什么
#include <stdio.h>
int main() 
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;
    printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
    return 0;
}

输出结果:

a=1

b=2

c=3

d=4

代码分析

为什么会出现这个结果呢?这是因为逻辑与(&&)一遇见假(0)等式就为假,不会在往后运算

所以a先使用为0,等式为假,然后再++,a为1,其他变量不会改变

  1. 同样的道理,下列代码输出什么
#include <stdio.h>
int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i=a++||++b||d++;
    printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
    return 0;
}

输出结果:

a=1

b=3

c=3

d=4

代码分析:

与逻辑与同样的道理,逻辑或(||)是一遇见真,整个表达式就为真,后面就不会执行

a先使用为0,++变为1,b先++变为3,在使用为真,后面表达式不执行

9. 条件操作符与逗号表达式

9.1 条件操作符

条件操作符又称为三目操作符,它的语法规则为:

如果条件为真 ? 则值为 X : 否则值为 Y

代码示例:

int main()
{
	int a = 1;
	int b = 1;
	int c = a == b ? 20 : 10;
	//含义:如果a=b为真,就将20赋给c,反之把10赋给c
	return 0;
}

9.2 逗号表达式

逗号表达式,就是⽤逗号隔开的多个表达式。

逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。

exp1, exp2, exp3, …expN

代码示例:

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("c=%d",c);
	return 0;
}

输出结果:

c=13

代码分析:

逗号表达式从左往右依次计算,a>b为假,a=b+10=12,a为12,b=a+1=13,再将13赋值给c

10. 下标引用和函数调用操作符

10.1 下标引用

下标引用一般是与数组有关,它一般有两个作用

int arr[10];//创建数组

arr[1]=10;//对数组赋值

10.2 函数调用

函数调用肯定是与函数相关,因为大家在前面学过函数,所以就简单举个例子

代码示例:

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 1;
	int b = 2;
	int ret = Add(a, b);
	return 0;
}

11. 结构体成员访问操作符

我们知道结构结构体访问一般有两种方式**,直接访问(.)与间接访问(->)**

11.1 用法

下表显示了 C 语言支持的结构体成员访问操作符。假设已定义结构体struct stu s则:

运算符 描述 实例
直接访问(.) 通过结构体变量直接访问其中成员 s.age=10
间接访问(->) 通过结构体变量地址间接访问其中成员 &s->age=11

11.2 代码实现

struct stu
{
	int age;
	char name[14];
};
int main()
{
	struct stu s;
	s.age = 10;
	(&s)->age = 11;
	return 0;
}

12. 操作符的优先级与结合性

12.1 介绍

优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。

结合性指的是如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。

下面是各种操作符的优先级与结合性

12.2 弊端

优先级和结合性其实是有弊端的,因为这两者都不能解决所有问题,比如说下面这段代码输出什么

#include <stdio.h>
int main()
{
     int i = 1;
     int ret = (++i) + (++i) + (++i);
     printf("%d\n", ret);
     printf("%d\n", i);
     return 0;
}

这其实是道错题,因为在不同编译器下结果是不同的。

VS 2022环境下:

gcc环境下:

  • 而且不得不吐槽一句,在公司里写代码哪个程序员会这样赋值写代码呀~