深度探索 C 语言操作符:从基础到实战应用

前言:

在 C 语言的编程体系中,操作符就像是一个个精密的齿轮,相互配合驱动着程序的运转。熟练掌握操作符的使用,不仅能编写出高效、简洁的代码,还能深入理解程序运行的底层逻辑。接下来,让我们一同深入探索 C 语言操作符的奥秘

目录

一、操作符的分类全景

二、算术操作符:数值运算的基石

三、移位操作符:二进制世界的舞者

四、位操作符:深入二进制的核心

五、赋值操作符:数据传递的纽带

[六、单目操作符:功能强大的 "独行侠"](#六、单目操作符:功能强大的 “独行侠”)

七、关系操作符和逻辑操作符:程序逻辑的导航仪

八、条件操作符和逗号表达式:灵活编程的秘籍

[九、下标引用、函数调用和结构成员操作符:C 语言中的重要工具](#九、下标引用、函数调用和结构成员操作符:C 语言中的重要工具)

[1、下标引用操作符 []](#1、下标引用操作符 [])

2、函数调用操作符 ()

[3、结构成员操作符 . 和 ->](#3、结构成员操作符 . 和 ->)

十、表达式求值:背后的规则与陷阱

一、操作符的分类全景

C 语言的操作符种类丰富,涵盖算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式,以及下标引用、函数调用和结构成员操作符等。它们各司其职,在不同的编程场景中发挥着关键作用

二、算术操作符:数值运算的基石

算术操作符是编程中最常用的操作符之一,包括+(加法)、-(减法)、*(乘法)、/(除法)和%(取余)。除了%操作符仅适用于整数运算外,其他操作符对整数和浮点数均适用,在进行除法运算时,若两个操作数都是整数,执行的是整数除法,结果会舍去小数部分。例如:

上述代码中,7 / 3的结果为2,因为整数除法会舍弃小数部分。

若操作数中有浮点数,则执行浮点数除法,结果会保留小数部分:

在这段代码里,7.0 / 3的结果为2.333333,这体现了浮点数除法的特性。

%操作符用于计算两个整数相除后的余数,例如:

这里7 % 3的结果是1,即7除以3的余数 。

三、移位操作符:二进制世界的舞者

移位操作符分为左移操作符 <<和右移操作符 >>,它们直接对二进制位进行操作。左移操作符的规则是左边抛弃,右边补 0。例如,将整数5(二进制表示为:00000000000000000000000000000101)左移一位:

执行代码后,shiftedLeft的值为10,因为左移一位后,二进制变成00000000000000000000000000001010,转换为十进制就是10

右移操作符的移位规则较为复杂,分为逻辑移位和算术移位。

逻辑移位:左边用 0 填充,右边丢弃;

算数右移:左边用原数的符号位填充,右边丢弃。例如,对于负数-1(补码为11111111111111111111111111111111):

警告⚠ : 对于移位运算符,不要移动负数位,这个是标准未定义的

四、位操作符:深入二进制的核心

位操作符包括按位与&、按位或|和按位异或^,操作数必须是整数。这些操作符在处理二进制数据时非常有用。

按位与操作可以用于判断一个数的某些位是否为 1。例如,判断一个整数的最低位是否为 1:

在这段代码中,num & 1num的二进制表示与00000000000000000000000000000001进行按位与操作。如果结果不为 0,则说明num的最低位是 1。

练习1 :不能创建临时变量(第三个变量),实现两个数的交换:

这段代码利用按位异或的特性,巧妙地实现了两个数的交换,避免了使用额外的临时变量,提高了代码的效率

练习2 :求一个整数存储在内存中的二进制中1的个数

五、赋值操作符:数据传递的纽带

赋值操作符=用于将右侧的值赋给左侧的变量,它还支持连续赋值。例如:

在这段代码中,a = b = c = 10从右向左依次赋值,最终abc的值都为10

此外,还有复合赋值符,如+=-=*=/=等,这些操作符可以简化代码。例如,a += 5等价于a = a + 5

使用复合赋值符不仅代码更简洁,还能提高编程效率。

六、单目操作符:功能强大的 "独行侠"

单目操作符只需要一个操作数,功能多样。例如,逻辑反操作!可以将真(非 0)变为假(0),将假变为真:

sizeof操作符用于计算操作数的类型长度(以字节为单位):

这里sizeof(num)返回num所占用的字节数,sizeof(char)返回char类型占用的字节数。不同系统下,这些值可能会有所不同,但在常见的 32 位和 64 位系统中,int通常为 4 字节,char为 1 字节

七、关系操作符和逻辑操作符:程序逻辑的导航仪

关系操作符用于比较两个值的大小关系,包括>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、!=(不等于)和==(等于)。在使用时,要特别注意===的区别,避免因写错而导致逻辑错误。例如:

这段代码通过关系操作符判断ab的大小关系,并根据判断结果执行相应的代码块。

逻辑操作符包括逻辑与&&和逻辑或||,用于组合多个条件。逻辑与操作只有当所有条件都为真时,结果才为真;逻辑或操作只要有一个条件为真,结果就为真。例如:

在这段代码中,&&||分别组合了多个条件,控制程序的执行流程

八、条件操作符和逗号表达式:灵活编程的秘籍

条件操作符exp1? exp2 : exp3是一种简洁的选择结构,根据exp1的真假来决定执行exp2还是exp3。例如,求两个数中的较大值:

这段代码使用条件操作符简洁地求出了ab中的较大值(如果a>b为真就执行a,反之,执行b)

逗号表达式exp1, exp2, exp3, ...expN从左向右依次执行,整个表达式的结果是最后一个表达式的结果。例如:

在这段代码中,逗号表达式先执行a++b++,然后计算a + b的值并赋给c。最终c的值为5,因为a++b++执行后,a变为2b变为3a + b即为5

九、下标引用、函数调用和结构成员操作符:C 语言中的重要工具

在 C 语言丰富的操作符家族中,下标引用、函数调用和结构成员操作符是进行数据访问和函数执行的关键元素,它们各自有着独特的功能和使用方式

1、下标引用操作符 []

下标引用操作符用于访问数组中的元素。它的操作数为一个数组名和一个索引值。

在 C 语言中,数组是一种重要的数据结构,用于存储多个相同类型的数据。通过下标引用操作符,可以方便地访问数组中的特定元素。例如:

在上述代码中,[] 的两个操作数分别是数组名 arr 和索引值 9。需要注意的是,C 语言中的数组索引是从 0 开始的,这意味着对于一个长度为 n 的数组,合法的索引范围是 0n - 1。如果使用了超出这个范围的索引,会导致未定义行为,可能引发程序崩溃或产生错误的结果。

此外,下标引用操作符还可以与指针结合使用。在 C 语言中,数组名在很多情况下会被隐式转换为指向数组首元素的指针。因此,arr[i]*(arr + i) 是等价的,它们都表示访问数组 arr 中索引为 i 的元素。例如:

2、函数调用操作符 ()

函数调用操作符用于调用函数。它接受一个或者多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

函数是 C 语言中实现模块化编程的重要手段,通过函数调用操作符,可以执行预先定义好的函数逻辑。例如:

在上述代码中,add 是函数名,作为函数调用操作符 () 的第一个操作数,35 是传给 add 函数的参数。函数调用操作符会触发函数的执行,函数执行完毕后会返回一个值(在这个例子中,add 函数返回两个参数的和),可以将这个返回值赋值给变量或者进行其他操作。

函数调用操作符的灵活性很高,不仅可以传递常量作为参数,还可以传递变量、表达式等。同时,函数也可以没有参数,例如:

在这个例子中,printHello 函数没有参数,调用时只需要使用函数名和空的函数调用操作符 () 即可。

3、结构成员操作符 .->

结构是 C 语言中用于将不同类型的数据组合在一起的一种自定义数据类型。结构成员操作符用于访问结构中的成员。

'.' 操作符 :当使用结构变量来访问成员时,使用 . 操作符。例如:

在上述代码中,stustruct Student 类型的变量,通过 . 操作符,stu.name 访问结构体中的 name 成员,stu.age 访问 age 成员

-> 操作符 :当使用指向结构的指针来访问成员时,使用 -> 操作符。例如:

在这个例子中,ptr 是指向 struct Student 类型的指针,通过 -> 操作符,ptr->nameptr->age 分别访问结构体 stu 中的 nameage 成员。实际上,ptr->name 等价于 (*ptr).name-> 操作符提供了一种更简洁的方式来通过指针访问结构体成员。

下标引用、函数调用和结构成员操作符在 C 语言编程中扮演着不可或缺的角色。熟练掌握它们的使用方法,能够帮助开发者更加高效地进行数据处理和函数调用,构建出结构清晰、功能强大的程序。

十、表达式求值:背后的规则与陷阱

表达式求值的顺序受到操作符优先级、结合性以及是否控制求值顺序等因素的影响。在进行表达式求值时,还可能涉及隐式类型转换和算术转换。

隐式类型转换是为了保证整型运算的精度,会将字符和短整型操作数在使用前转换为普通整型。例如:

字符 'A''B' 在参与 a + b 运算时,由于 C 语言的整型算术运算总是至少以缺省整型类型(通常为 int 类型)的精度来进行,所以会先进行整型提升。'A' 的 ASCII 码值是 65,'B' 的 ASCII 码值是 66 ,它们在进行整型提升时,会将其对应的 ASCII 码值按照 int 类型的长度进行扩展:

  • 对于有符号 char 类型(很多编译器下默认 char 是有符号的),如果 char 类型的最高位(符号位)为 0,就在高位补 0;如果最高位为 1,就在高位补 1(有符号位补符号位)。
  • 对于无符号 char 类型,无论原 char 类型数据的最高位是什么,都在高位补 0(没符号位补0)。

这里 'A''B' 的 ASCII 码值对应的二进制最高位都是 0,所以整型提升后高位补 0,然后再进行加法运算,65 + 66 = 131,最终 result 的值为 131。
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算

但要注意,不合理的算术转换可能会导致精度丢失等问题。例如:

在这段代码中,float类型的f转换为int类型时,小数部分会被截断,导致精度丢失。

有些表达式由于操作符优先级和结合性无法确定唯一的计算路径,结果是不可预测的,属于有问题的表达式。例如:

在不同的编译器中,这段代码的结果可能不同,因为i++++i的执行顺序无法通过操作符的优先级和结合性确定。在编写代码时,应尽量避免写出这样的表达式,确保程序的正确性和稳定性。

C 语言操作符是编程中不可或缺的一部分,深入理解和熟练运用它们,能够帮助我们编写出更加高效、稳定的程序。希望通过本文的介绍,大家对 C 语言操作符有更全面、更深入的认识,在编程实践中能够灵活运用这些知识,解决各种实际问题。

相关推荐
神仙别闹4 小时前
基于 Java 的 C 语言编译器
java·c语言·开发语言
魂兮-龙游5 小时前
C语言:把两个16位的数据合成32位浮点型数据
c语言·前端·javascript·信息与通信
和风化雨5 小时前
排序算法--计数排序
c语言·数据结构·c++·算法·排序算法
我命由我123456 小时前
游戏引擎 Unity - Unity 打开项目、Unity Editor 添加简体中文语言包模块、Unity 项目设置为简体中文
c语言·开发语言·c++·unity·ue5·c#·游戏引擎
阿巴~阿巴~8 小时前
C_数据结构(队列) —— 队列的初始化、入队列队尾、队列判空、出队列队头、取队头队尾数据、队列有效元素个数、销毁队列
c语言·网络·数据结构·算法·排序算法
小仇学长8 小时前
嵌入式八股文面试题(一)C语言部分
c语言·c++·面试·嵌入式·八股文
比特在路上8 小时前
蓝桥杯之c++入门(一)【C++入门】
c语言·c++·蓝桥杯
没有名字的鬼9 小时前
C_位运算符及其在单片机寄存器的操作
c语言·开发语言
魂兮-龙游9 小时前
C语言:将四个八位无符号数据拼接成32位的float数据
c语言·开发语言·算法·数据分析