ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C语言入门,操作符知识讲解

C语言入门,操作符知识讲解 目录二进制和进制转换1.2进制转10进制2.10进制转2进制3.2进制转8进制4.2进制转16进制原码、反码、补码移位操作符1.左移操作符: 2.右移操作符 位操作符 、| 、^ 、~单目操作符逗号表达式下标访问[ ]、函数调用()1.下标访问[ ]2.函数调用()结构成员访问操作符操作符的属性优先级、结合性1.优先性2.结合性表达式求值1.整型提升2.算术转换3.问题表达式解析4.总结前言在 C 语言的学习中操作符运算符是构建一切表达式的基础也是很多初学者容易混淆、踩坑的地方。无论是简单的算术运算还是复杂的位运算、表达式求值背后都离不开对操作符规则的深入理解。本文是一篇面向 C 语言初学者的操作符系统总结内容覆盖了从进制转换、原码反码补码到移位操作符、位操作符、单目操作符、逗号表达式再到结构成员访问、操作符的优先级与结合性以及表达式求值中的整型提升与算术转换等核心知识点。通过本文你将能够掌握二进制、八进制、十进制、十六进制之间的转换方法理解整数在内存中的存储形式原码、反码、补码熟练运用移位操作符与位操作符解决实际问题了解操作符的优先级与结合性对表达式求值的影响认识整型提升与算术转换并学会规避复杂表达式带来的歧义。建议在学习过程中结合文中的示例代码与图示亲自动手在编译器中运行验证这样才能真正把知识内化。下面让我们从最基础的进制转换开始吧。1.二进制和进制转换我们经常会听到二进制、八进制、十进制、十六进制的讲法但是这分别是什么意思呢实际上二进制、八进制、十进制、十六进制是数值不同的表现形式而已举个例子我们将数值15写成不同进制下的表示形式15的2进制111115的8进制1715的10进制1515的16进制F我们或许了解一些关于进制的知识二进制每一位数字由0或1构成满2进1八进制每一位数字由0到7范围内的数字构成满8进1十进制每一位数字由0到9范围内的数字构成满10进1十六进制每一位数字由0到9、A到F范围内的数字构成满16进1A到F对应数字10到15不区分大小写举例如下小tips//16进制数值前写0x//8进制数值前写0举个例子一下这个标准1.2进制转10进制10进制的123表示一百二十三为什么是一百二十三呢10进制的每一位是有权重的从右到左依次为个位、十位、百位……分别对应权重并计算如下图进制之间都是类似的只不过是权重发生了变化。如果是2进制的1101该如何理解呢2.10进制转2进制这里采用的方法是取余法举个例子将10进制的125转换为2进制表示如下图将125连续除2取余数直到除尽从下往上读取余数就是10进制的125对应的2进制表示形式。再介绍一个方法凑数法用于一个数的值很小时举个例子将10进制的35转换为2进制的表示形式如下图我们最终的目的是通过2进制的权重凑出35首先取与35接近的数字32还剩下3取个1和2就凑出来了其余补0得到二进制的表达形式为100011。3.2进制转8进制8进制的数字每一位是0~7的数字各自写成2进制最多有3个2进制可以表示一个数字在2进制转8进制数时从2进制序列中右边低位开始向左每3个2进制位会换算1个8进制位剩余不够3个2进制位的直接换算。举个例子将2进制的01101011换成8进制0153开头0作为8进制的标识若是8进制转2进制反着来将8进制的每一位拆成3个二进制位就可以了。4.2进制转16进制16进制的数字每一位是0到9、a到f的数字各自写成2进制最多有4个2进制可以表示一个数字在2进制转16进制数时从2进制序列中右边低位开始向左每4个2进制位会换算1个16进制位剩余不够4个2进制位的直接换算。举个例子将2进制的01101011换成16进制0x6b开头0x作为16进制的标识8进制的数字每一位是0~7的数字各自写成2进制最多有3个2进制可以表示一个数字在2进制转8进制数时从2进制序列中右边低位开始向左每3个2进制位会换算1个8进制位剩余不够3个2进制位的直接换算。举个例子将2进制的01101011换成8进制0153开头0作为8进制的标识同样的若是16进制转2进制反着来将16进制的每一位拆成4个二进制位就可以了。16进制转8进制、8进制转10进制……都先转化为2进制再按需求进行转化就行了。2.原码、反码、补码计算机能够识别的是2进制的信息而数据在内存中存储的时候也是以2进制的形式存储的。整数的2进制表示方法有3种即原码、反码和补码无符号整数无符号位所有位上的数都是数值位。有符号整数的三种表示方法均有符号位和数值位在2进制序列中最高位的1位在序列的最左边的1位作为符号位其余的都是数值位。符号位上的数0为正数1为负数如下图对于原码、反码、补码在整数的应用进行说明原码直接将数值按照正负数的形式翻译成二进制得到的就是原码。反码将原码的符号位不变其他位依次按位取反就可以得到反码。补码反码1就得到补码。补码得到原码法1按照原码得到补码的步骤反过来补码-1得到反码反码符号位不变取反得到原码。法2取反1的操作。该方法不经过反码直接求原码。举例对于整型来说数据存放在内存中的信息都是补码。为什么因为使用补码时可以将符号位和数值位同时处理直接按正常的加减运算去做补码和补码算出的结果还是补码。内存存的和计算使用的都是补码但是打印在屏幕上的原码。由于cpu只有加法器如1-1转化成1(-1)的形式来运算如下图3.移位操作符注操作数只能是整数而且移动的是整数的二进制位补码。1.左移操作符: 移位规则左边抛弃右边补0举个例子上述代码中移位操作符只会a移位后的结果赋给b但不会改变移位前的数据a。2.右移操作符 移位规则右移运算分为2种1.逻辑右移左边用0填充右边丢弃2.算术右移左边用原该值的符号位填充右边丢弃。原来是负数补1原来是正数补0。右移到底用逻辑右移还是算数右移呢这个是取决于编译器的大部分编译器上都是采用算术右移举个例子上述代码说明VS2022的编译器采用的是算术右移。警告1.对于移位运算符不要移动负数位这个是标准未定义的。intnum10;num-1;2.移动的位数超过32位也是不行的。4.位操作符 、| 、^ 、~注双目操作符操作数必须为整数而且参与运算的是整数的二进制补码。1.//按位与2.|//按位或3.^//按位异或4.~//按位取反按位与按位或按位异或按位取反了解位操作符的规则之后我们来一道练习题吧!题目不能创建临时变量第三个变量实现两个整数的交换。在之前我们写过用第三个变量去实现题目的要求但是题目要求不能创建第三个变量我们会想到用2个变量去做该代码有局限性假如ab的值很大时一个整型占4个字节最高位留给符号位还剩31位都放不下时数据存在溢出的风险说明了该代码是由bug的。但是今天我们学习了位操作符后提供给大家一个新思路去做我们根据这个思路写出以下代码但是在日常生活中第1种方法实际上效率是最高的我们通常会采用第1种方法第3种只是给大家提供一个思路很少用。紧接着我们再做一道练习吧题目编写代码实现求一个整数存储在内存中的2进制中1的个数。思路实现代码实现上述代码在调试的过程中对于15的2进制位上的1求到的个数为4但是对于-1的2进制位上1的个数求到的个数为0很显然我们的代码好像面对正数时可以计算出正确结果但是面对负数时却计算出错事实上n-1进入循环n/2使n变成0了这时候我们找出了错误的原因那我们如何改进代码才能实现负数层面的正确计算呢思路实现代码实现以上代码可以满足题目的要求接下来介绍一种新的算法在这道题的运用思路实现:上述代码思路是通过n(n-1),每执行一次从最右边往左边去掉2进制位上的一个1对比上一个代码进行了进一步的优化我们根据以上思路写出以下代码这个思路原本是用来判断n是否2的n次方数这道题目的思路就是利用nn(n-1),判断2进制位中只要包含一个1就属于2的次方数判断条件为执行次数为1次或n(n-1)0执行一次去掉一个1之后就变0了。紧接着我们再来一个练习题目二进制位置0或置1编写代码将13的2进制序列的第5位修改为1然后再改回0从右往左数的第5位思路实现代码实现5.单目操作符单目操作符有这些、、–、、*、、-、~、sizeof、(类型)单目操作符的特点是只有1个操作数在单目操作符中只有和星号没有介绍这两个操作符后续在学习指针时学习。6.逗号表达式用逗号隔开的多个表达式从左往右执行前面的计算结果可能会影响到后面整个表达式的结果就是最后一个表达式的结果。举个例子上述代码从左往右执行b13,将b的值赋给c,得到c13在实际中逗号表达式还可以优化代码优化前优化后7.下标访问[ ]、函数调用()1.下标访问[ ]2.函数调用()8.结构成员访问操作符在今天我我们会学到“.”这个操作符“-”这个等指针讲完之后来学习。在C语言中已经提供了内置类型如int、char等但这些内置类型是不够的而结构体作为一种自定义类型让程序员可以创造出自己所需要的类型。如描述一个学生需要名字、年龄、学号、身高、体重等。结构是一些值的集合这些值叫做“成员变量”每个成员变量可以是不同类型的变量如数组、指针、枚举、联合体……结构的声明举个例子调试的结果在初始化的时候不按照结构体内成员变量的顺序也可以进行输入首先输入一个“.”随后会显示选项双击选择你想要对应的信息输入就可以了如下再举个例子通讯录存储人的名字、年龄、电话号码运行结果结构体成员的直接访问点操作符.需要2个操作数使用方式结构体变量.成员名9.操作符的属性优先级、结合性在C语言中有2个属性决定了表达式求值的计算顺序优先性、结合性1.优先性在一个表达式中包含多个运算符哪个运算符优先执行各个运算符的优先性不一样。如345由于的优先级比高所以先计算乘法后计算加法2.结合性若两个运算符优先级相同时这时候就要看结合性了根据运算符是从左往右执行还是从右往左执行。下面列举经常使用的运算符的优先级从上至下优先级递减·圆括号()·自增运算符()、自减运算符(–)·单目运算符(和-)·乘法(*)、除法(/)·加法()、减法(-)·关系运算符(、等)·赋值运算符()其他的查询一下link最懒的方法直接加括号决定计算顺序10.表达式求值1.整型提升C语言中整型算术运算中总是以编译器默认的整型类型来进行的。为了获得这个精度表达式中的字符和短整型操作数在使用之前会被转换成普通整型这种转换称为整型提升。举个例子计算出的结果是什么为什么是这个数首先我们知道char的取值范围是-128~127存不下140紧接着发生过程如下图那么如何去做整型提升呢1.有符号整数提升是按照变量的数据类型的符号位来提升的2.无符号整数提升高位补0我们回到a和b的整型提升上来2.算术转换上述代码是2个字符类型相加属于相同类型的运算如果遇到某个操作符的各个操作数属于不同的类型那么此时就要把其中一个操作数的转换为另一个操作数的类型否则无法进行下去。下面的层次体系称为寻常算术转换:1.long double2.double3.float4.unsiged long int5.long int6.unsiged int7.int如果某个操作数的类型在上面这个排序中排名靠后那么首先要转换为另一个排名靠前的操作数类型后再进行运算。算术转换针对的类型最小也是int所以该过程是在整型提升之后进行的。在学习完各种操作符及属性、整型提升、算术提升后我们是不是可以写出一个表达式就能确定唯一的计算路径计算出结果呢其实还不行为此举出下面几个问题表达式去说明。3.问题表达式解析1.表达式1a*bc*de*f我们若根据操作符的优先级来说有2种符合规则的计算路径路径11.a*b2.c*d3.1和2相加4.e*f5.3和4相加路径21.a*b2.c*d3.e*f4.1和2相加5.3和4相加2.表达式2c--c操作符的左操作数c的获取在–之前还是之后求值所以结果是不可预测的是有歧义的。3.表达式3该表达式在不同的编译器上计算出的结果不同4.表达式4虽然这段代码在大部分编译器上运行的结果相同上述代码anwer fun()-fun()*fun()根据操作符的优先级我们知道先算乘法后算减法但是函数的调用不能用优先级决定我们不知道3个函数哪一个先被调用。5.表达式5这段代码在不同的编译器上计算的结果不同无法确定正确答案。4.结论即使我们了解操作符的规则、属性、整型提升等知识我们写出的表达式也可能无法确定唯一的计算路径去得到正确的结果建议就是不要写太复杂的表达式。总结本文系统梳理了 C 语言中操作符相关的核心知识主要涵盖以下几个方面1. 进制转换二进制、八进制、十进制、十六进制只是数值的不同表现形式。掌握 2 进制与 10 进制、8 进制、16 进制之间的相互转换取余法、凑数法、按位分组法是理解计算机底层数据表示的基础。2. 原码、反码、补码整数在内存中以补码形式存储。原码、反码、补码的相互转换规则以及补码能将符号位与数值位统一处理的优势是理解整数运算的关键。3. 移位操作符左移与右移操作的是整数的二进制补码。左移右边补 0右移分为逻辑右移与算术右移多数编译器采用算术右移。注意不要移动负数位或超过 32 位。4. 位操作符按位与、按位或|、按位异或^、按位取反~均作用于整数的二进制补码。通过位操作可以实现不借助临时变量交换两个整数、统计二进制中 1 的个数、将指定位置 0 或置 1 等经典技巧。5. 其他操作符单目操作符、逗号表达式、下标访问[]、函数调用()、结构成员访问.与-等各有其使用场景与注意事项。6. 操作符的属性与表达式求值操作符的优先级与结合性决定了表达式的计算顺序。整型提升与算术转换是表达式求值前的重要预处理步骤。对于复杂表达式由于求值顺序可能存在歧义建议不要写过于复杂的表达式必要时用括号明确计算顺序。总的来说操作符是 C 语言表达式的基石理解其规则与底层原理不仅能帮助你写出更高效、更安全的代码也能为后续学习指针、结构体、函数等进阶内容打下坚实基础。希望本文能对你的 C 语言学习有所帮助
返回列表