初始C语言——运算符
接下来我们将继续探讨C语言的基础知识。上一章讲解了数据类型的概念本章将重点介绍运算符的相关内容。让我们直接进入主题开启C语言运算符的学习之旅。各类数值型数据间的混合运算隐式转换显示转换C运算符和C表达式算术运算符赋值运算符关系运算符逻辑运算符逗号运算符位运算各类数值型数据间的混合运算首先大家先可以知道整型、浮点型、字符型数据可以进行混合运算如10-‘a*1.510-97*1.5//保证参与运算的都是数字10.0-97.0*1.5//临时申请一块空间保证运算数据类型一致、运算完毕临时空间回收隐式转换解释整型、浮点型、字符型能进行混合运算核心原因是它们都属于数值型数据——字符本质是特殊的数值型参与数值计算时使用ASCll码取值范围是0~127运算规则若参与运算的两个数类型不同会先将类型转换为一致后再运算。转换规则分为隐式转换和显示转换。隐式转换隐式转换又被称为自动类型转换由编译系统自动控制规则是将低等级提示为高等级类型。转换关系由低到高如图所示注①int → unsigned int → long → doubleint与unsigned的转换顺序可能因平台如操作系统位数和编译器不同存在差异需结合具体环境判断。②char /short →int必然转换​③float → double 必然转换C语言默认以双精度浮点型double进行浮点运算因此float类型参与运算时会自动隐式转换为double关键注意事项混合运算中的类型转换仅在运算过程中临时生效不会改变原变量的类型和内存的存储形式所谓类型提升其实就是系统运算时将原本的数据产生一份副本由副本数据参与计算计算完毕副本销毁原数据不受影响举例inta10;//a的类型始终是intdoubleca22.5;//运算时a临时转换为double等价于10.0 22.5其实就是a的double类型的副本参与运算a21;//a仍然是int变量一旦申请其内存大小不会发生改变显示转换显示转换又被称作强制类型转换由程序员手动指定转换类型。语法说明doublea//将变量 a 的值强制转换为double类型。​intxy//将表达式 xy 的结果转换为int类型。括号比较包含整个表达式否则转换xintxy//仅将变量x转换为int类型在于y进行计算括号仅作用于x举例doublea2,b3;doublec(int)ab;//第1步显示将a转换为int(2)第2步混合运算int转化为double最终等价于2.0 3.0doublenum112.55;intnum2(int)num1;//显示将num1转换为int类型浮点型转整型舍弃小数部分不是四舍五入printf(%d\n,num2);num115;关键注意事项1、强制类型转换仅在运算过程中临时改变值的类型不会修改原变量的类型和内存存储形式2、浮点型转整型时直接舍弃小数部分不进行四舍五入3、转换后的类型需与接收变量的类型匹配避免数据溢出或精度丢失。C运算符和C表达式C运算符C表达式由运算数常量、变量、表达式和运算符按照C语法规则连接而成的式子表达式 运算数 运算符名称举例结果说明与说明算术表达式2 6.7 *3.5数值类型(int或double等取决于运算数类型)关系表达式x 0 、y z6布尔类型C语言用0表示假非0表示真逻辑表达式x 0 y 0布尔类型赋值表达式a 5.6、sum i、a b c 5左侧变量的类型运算顺序自右向左逗号表达式x 3, y 4 , z - 8最后一个表达式的类型结果为最后一个表达式的值注注意逗号表达式和逗号分隔符。逗号运算符在表达式中用来连接多个表达式有 “顺序执行 返回最后一个值” 的作用比如 (a1, b2)。逗号分隔符用来分隔函数参数、变量声明等没有 “逗号表达式” 的特性比如 printf(“%d”, a) 里的逗号或者 int a, b, c; 里的逗号逗号表达式 x 3, y 4 , z - 8 最后一个表达式的类型结果为最后一个表达式的值C语言运算符优先级与结合性C语言通过优先级和结合性规定表达式的求值顺序优先级高的运算符先执行优先级相同时按结合性顺序执行。简化优先级表从高到低优先级运算符类别运算符示例结合性1括号、下标、成员访问()、[]、.、-从左向右2后缀自增 / 自减a、a--从左向右3前缀自增 / 自减、逻辑非等a、--a、!、sizeof、、-负号从右向左4强制类型转换(type)从右向左5算术乘除、取余*、/、%从左向右6算术加减、-从左向右7位左移 / 右移、从左向右8关系运算符比较大小、、、从左向右9关系运算符相等判断、!从左向右10位与从左向右11位异或^从左向右12位或|从左向右13逻辑与从左向右14逻辑或||从左向右15条件运算符?:从右向左16赋值运算符含复合赋值、、-、*、/等从右向左17逗号运算符,从左向右算术运算符运算符功能类型注意事项加法/正值双目/单目单目时表示正值如5-减法/负值双目/单目单目时表示负值如-5*乘法双目无/除法双目除数不能为0整型相除结果为整型如1/20%取余双目仅适用于整型结果符合与被除数一致自增单目是变量值增1–自减单目使变量值减1面试题1/21/2的结果是多少答案是0两个整数相除结果为00001.0/21.0/2.0的结果是多少答案是1.0两个浮点型运算0.50.51.0自增、自减运算符/–作用使变量值增1或者减1适用于算术类型整型、字符型、浮点型等的可修改左值实践中常用整型和字符型变量。核心区别运算符位置绝对变量值的更新时机。i/–i前缀自增/自减运算规则先更新变量值后使用变量先计算后使用自己计算别人使用​ 步骤一变量值自增1i i1或自减1i i-1​ 步骤二使用更新后的变量值参与计算、赋值、比较等操作。i/i–后缀自增/自减运算规则先使用变量后更新变量值先使用后计算​ 步骤一使用变量当前值参与赋值、计算、比较等操作。​ 步骤二变量值自增1i i1)或自减1i i-1赋值运算符基本赋值运算符作用将右侧运算数常量、变量、表达式的值存入左侧变量的内存单元。核心规则1、运算顺序自右向左先计算右侧表达式再赋值给左侧变量2、左侧必须是可修改的变量左值不能是常量或表达式如5 a、a b 3均为非法3、赋值表达式的值等于左侧变量的最终值如 b a 5 中a 5 的值为5再赋值给b最终b 5.类型转换规则赋值时若赋值运算符 两侧类型不一致会自动进行类型转换转换规则如下源类型目标类型转换规则举例浮点型{double、float}整型short、int、long舍弃小数部分、仅保留整数部分int a 5.9 → a 5整型short、int、long浮点型double、float数值不变以目标浮点型格式存储double b 5 → b 5.000000字符型char整型short、int、long字符的ASCll码存入整型低8位高位补0int c ‘A’ → c 65长整型long long短整型截取低字节数据可能导致数据溢出short d 32768int→溢出复合赋值运算符复合赋值运算符是基本赋值运算符与算术运算符/位运算的结合简化代码书写常见复合赋值运算符运算符等价形式举例结果a a bint a1; a3;a413-a a - bint a5; a-2;a35-2*a a * bint a3; a*4;a123*4/a a / bint a8; a/2;a48/2%a a % bint a7; a%3;a17%3a a bint a5; a6;a456|a a| bint a5;a|6;a7(5|6)^a a ^ bint a5; a^6;a35^6a a bint a2; a1;a421a a bint a4; a1;a241关系运算符作用比较两个运算数的大小或相等关系结果为布尔类型C语言用0表示假非0表示真常用关系运算符均为双目运算符运算符功能举例结果假设a5b4大于a b1(true-真)小于a b0(false-假)大于等于a51(true-真)小于等于b30(false-假)等于a 51true-真不等于a b1true-真关键注意事项1、避免链式比较C语言不推荐 0 score 100逻辑比较这类链式写法逻辑上会报错编译不报错。​ 错误原因编译器按左结合性依次计算如 0 score 100等价于0 score) 100,结果永远为真无论score是否在0~100范围内。​ 正确写法使用逻辑与运算符 score 0 score 1002、浮点型相等比较需要用差值法介绍由于浮点型存储精度限制如1.1无法精确存储直接用 判断相等会导致逻辑错误。错误示例floata1.1f1.2f;// 2.300000floatb2.3f;printf(a%.20f,b%.20f,%d\n,a,b,ab);//a2.30000019073486328125,b2.29999995231628417969,0正确写法判断两数差值的绝对值是否小于极小值如1e-6即0.000001需引入math.h头文件使用fabs函数取绝对值#includemath.h#defineEPSle-6//定义误差允许范围if(fabs(a-b)EPS){//并不是真实的相等只是判断两个数差值的绝对值是否在可接受的范围内}浮点型比较总结逻辑运算符作用布尔类型的运算数进行逻辑运算结果仍然为布尔类型0表示假非0表示真运算符名称类型运算规则短路效果逻辑非单目真→假假→真无仅一个运算数逻辑与双目全1则1有0则0左侧为假时右侧不执行||逻辑或双目全0为0有1唯1左侧为真时右侧不执行逻辑非 !仅作用于右侧运算数优先级较高​仅同一运算数取非奇数次结果与原值相反取非偶数次结果与原值相同。举例inta5,b0;printf(%d\n,!a);// 0a非0为真!真假printf(%d\n,!b);// 1b0为假!假真printf(%d\n,!(a%2!0));// 0a%2!0为真!真假printf(%d\n,!!a);// 1!!真真自右向左逻辑与运算顺序自左向右​短路效果若左侧运算数表达式为假右侧运算数不再执行因最终结果必为假无需计算右侧。举例intscore90;printf(%d\n,score0socre100);intx3,y5;if(xyy)//此时触发短路{//不执行}printf(y%d\n,y);//输出 y 5逻辑或||​ 运算顺序自左向右​ 短路效果若左侧运算数表达式为真右侧运算数不再执行因最终结果必为真无需计算右侧。举例intyear2025;//闰年判断能被4整除且不能被100整除或者能被400整除printf(%d\n,(year%40year%100!100)||year%4000)逗号运算符作用将多个表达式串联成一个表达式又称作顺序求值运算符按从左到右的顺序执行每一个表达式。核心规则​ 1、求值顺序从左往右依次计算每个表达式的值​ 2、最终结果整个逗号表达式的值等于最后一个表达式的值。​ 3、优先级逗号运算符的优先级是所有运算符中优先级最低的。需要注意括号的使用。举例#includestdio.hintmain(){//变量函数中的逗号叫做逗号分隔符inta0,b0;//整体是赋值表达式(a 3,b 5, a b)内是逗号表达式结果是最后一个表达式的结果8intresult(a3,b5,ab);//根据条件判断的逗号表达式intx10,y20;intmax(x,y,(xy)?x:y);//这个整体是赋值表达式只有后面(x,y,(xy) ? x : y)才是逗号表达式}建议如果无法区分逗号表达式和赋值表达式就看谁最后执行谁最后执行就是谁位运算记住在计算机中参与运算的都是补码正数原码反码补码作用直接对数据的二进制位bit进行运算常用于嵌入式开发、底层编程、数据加密等场景。前提参与位运算的操作数需要先转换为二进制signed类型按补码存储、unsigned类型按原码存储。按位取反~​ 类型单目运算符只有一个运算数​ 规则对数据的每一个二进制位取反0→11→0​ 注意整数在内存中以补码形式存储取反需要结合补码规则转换为十进制如~5 -6演示以8位二进制为例十进制二进制原码按位取反十进制结果50000 01011111 1010-6举例printf(%d\n,~5);//输出-632位系统中补码运算结果按位与​ 类型双目运算符​ 规则对应二进制位均为1时结果为1否则为0有0则0演示案例以8位二进制为例十进制二进制补码运算十进制二进制补码结果二进制结果十进制50000 010160000 01100000 01004举例printf(%d\n,56);常用场景1、按位清零将特定位设为0如num 0xFFFFFFF将num的最低设置为0后续展开讲解2、提取特定位获取数据的某几段如num 0x0F提取num的低4位寄存器、flash相对大一点的数据、sdcard持久化的文件按位或|​ 类型双目运算符​ 规则对应二进制位有一个为1时结果为1否则为0有1则1演示案例以8位二进制为例十进制二进制补码运算十进制二进制补码结果二进制结果十进制50000 010160000 01100000 01117举例printf(%d\n,5|6);//7按位异或^​ 类型双目运算符​ 规则对应二进制位相同为0不同为1相同为0不同为1演示案例以8位二进制为例十进制二进制补码运算十进制二进制补码结果二进制结果十进制50000 0101^60000 01100000 00113常用场景​ 1、数据交换无需临时变量交换整数a a ^ b; b a ^ b;a a ^ b​ 2、按位翻转将特定为翻转0→11→0​ 3、加密解密与同一个秘钥异或两次可以还原数据。面试题不使用临时变量如何实现两个变量的交换提示交换变量有3种方法①创建临时变量②使用函数传址方式③使用位运算中的按位异或这里介绍第三种答无需临时变量交换整数a a ^ b; b a ^ b;a a ^ b易错点在进行一正一负时首先搞清楚正数原码反码补码如果算出来补码首位是0那就是正数直补码反码如下图所示按位左移说明将原操作数的所有二进制位整体 向左移动指定的位数。移位规则为“高舍低补”高位溢出的二进制位直接丢弃低位补0。该规则与存储模式大/小端无关仅取决于移位操作本身。无符号左移核心公式无溢出时结果等价于操作数∗2移动位数操作数 * 2^{移动位数}操作数∗2移动位数溢出后公式失效​ 举例unsignedchara33;//结果为243*2^324)unsignedintb54;//结果为805*2^480)推导有符号左移注意是通过补码的左移低位补0最后在转换为原码​ 语法同上​ 核心公式同上​ 举例chara-33;//结果为-24-3 * 2 ^ 3 -24intb2402;//结果为960240 * 2 ^2960)推导8位有符号char-33步骤二进制8 位说明原数 -3原码10000011符号位 1负数值位0000011反码11111100原码符号位不变数值位取反补码11111101反码 1运算基于补码左移 3 位补码11101000高位溢出丢弃低位补 0移位后反码11100111补码 - 1还原反码移位后原码10011000反码符号位不变数值位取反结果-24原码10011000对应十进制-24注意1、有符号数的移位基于补码进行运算过程原码→反码→补码→移位→反码→原码→结果​ 2、若移位后符号位被覆盖如正数左移后溢出变为负数公式不在适用结果由补码规则决定。反例如char127左移1位通过公式计算127*2^1254实际通过补码规则计算结果为-2按位右移)说明将原操作数的所有二进制位整体向右移动指定的位数。移位规则为“高补低舍”低位溢出的二进制位直接丢失高位由操作数类型决定补值。操作数高位补值规则移位类型无符号数补0逻辑右移有符号数补符号位1补10补0算术右移注意大部分编译器如GCCClangMSVC对有符号数均采用“算术右移”极少数特殊平台例外。核心公式结果等价于操作数/2移动位数操作数/2^{移动位数}操作数/2移动位数向下取整注意向下、向上、四舍五入无符号右移​举例unsignedchara33;//结果为03/2^30.375向下取整为0推导有符号右移​举例chara-33;//结果为-1-3/2^3-0.375,向下取整为-1推导8位有符号char演示-3 3移位运算完整流程原数据→二进制原码→二进制反码→二进制补码→进行移位操作→二进制反码→二进制原码→目标进制结果C语言运算符的内容到这里就结束了。如果文章里有什么讲得不够清楚或者有错误的地方还请大家多多担待。欢迎在评论区留言交流咱们下一章「分支与循环」再见

相关新闻

最新新闻

日新闻

周新闻

月新闻