news 2026/10/31 2:17:46

C语言学习笔记——常见操作符和表达式求值

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言学习笔记——常见操作符和表达式求值

C语言学习笔记——常见操作符和表达式求值

Hello大家好,又是我Mii,最近几天Mii又学习了常见操作符和表达式求值,所以Mii我又来写一篇文章来分享Mii学习到的知识了,话不多说直接开写(`・ω・´)
上次Mii学习了常亮和变量的知识,但是如果要完成一些运算,还得掌握常见的操作符,下面是C语言中提供的的操作符。
操作符也被叫做运算符,是不同的翻译,意思是一样的。

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

这里Mii主要学习了一些常用的操作符~
1.算数操作符
在C语言中有一组操作符叫:算数操作符。分别是+ - * / %,这些操作符都是双目操作符。
1.1+和-
+和-用来完成加法和减法。
+和-都是有两个操作数的,位于操作符两端的就是它们的操作数,这种操作符也叫双目操作符。

#include<stdio.h>intmain(){intx=4+22;inty=61-23printf("%d\n",x);printf("%d\n",y);return0;}

1.2
运算符用来完成乘法。

#include<stdio.h>intmain(){intnum=5;printf("%d\n",num*num),//输出25return0;}

1.3 /
运算符/用来完成除法。
除号的两端如果是整数,执行的是整数除法,得到的结果也是整数。

#include<stdio.h>intmain(){flaot x=6/4;inty=6/4;printf("%f\n",x);//输出1.000000printf("%d\n",y);//输出1return0;}

上面的示例为什么变量x的类型是float(浮点数),但是6/4得到的结果是1.0,而不是1.5这是为什么呢(´・ω・`?)
原因就在于C语言里面的整数除法是整数,只会返回整数部分,丢弃小数部分。

如果希望得到浮点的结果,两个运算数必须至少有一个浮点数,这时C语言就会进行浮点数除法。

#include<stdio.h>intmain(){flaot x=6.0/4;//或者写成6/4.0printf("%f\n",x);//输出1.500000return0;}

上面示例中,6.0/4表示进行浮点数除法,得到的结果就是1.5。

再看一个例子:

#include<stdio.h>intmain(){intscore=5;score=(score/20)*100;printf("%d\n",score);return0;}

上面的代码,Mii一开始经过运算以为score会等于25 ᗜˬᗜ,可是运行程序后发现score等于0(;﹏;),这是为什么呢(˘•ω•˘)?
这是因为score/20是整除,会得到一个整数值0,所以乘以100后得到的也是0。
为了得到预想的结果,可以将20改成20.0,让整除变成浮点除法。

#include<stdio.h>intmain(){intscore=5;score=(score/20)*100;printf("%d\n",score);return0;}

1.4 %
运算符%表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。

#include<stdio.h>intmain(){intx=6%4;//2return0;}

负数求模的规则是,结果的正负号由第一个运算数的正负号决定。

#include<stdio.h>intmain(){printf("%d\n",11%-5);//1printf("%d\n",-11%-5);//-1printf("%d\n",-11%5);//-1return0;}

上面的示例中,第一个运算符的正负号(11或-11)决定了结果的正负号。
通过以上的学习Mii写了个计算任意两个整数的平均值,结果是小数٩(๑˃̵ᴗ˂̵)۶
想要计算任意两个整数的平均值,那么我们需要给程序输入两个值,这里就需要scanf函数;scanf函数是格式化的输入函数。什么是格式化?什么是输入呢?


代码中的%d是占位符,对应的是整数类型,用在printf函数中表示要打印一个整型类型的数据;用在scanf函数中表示从接受从键盘输入的一个整形类型的值。

%d//对应int类型%f//输出时对应float和double,输入时%f对应float类型,%lf对应double%c//对应char类型%s//对应字符串

下面是程序的代码:

#include<stdio.h>intmain(){inta=0;intb=0;//输入2个整数scanf("%d%d",&a,&b);//计算floatavg=(a+b)/2.0;//输出结果printf("%f\n",avg);return0;}

2. 赋值操作符:=和复合赋值
在变量创建的时候给一个初始值叫初始化,在变量创建好后,再给一个值,这叫赋值。

inta=100;//初始化a=100;//赋值,这里使用的就是赋值操作符

赋值操作符=是一个随时可以给变量赋值的操作符。

2.1 连续赋值符
赋值操作符也可以连续赋值,如:

inta=3;intb=5;intc=0;c=b=a+3;

C语言虽然支持这种连续赋值,但是写出的代码不容易理解,Mii在这里更愿意拆开来写,这样方便观察代码的执行细节。

int a=3;int b=5;int c=0;b=a +3;c=b;

这样写,在调试的时候,每一次赋值的细节都是可以很方便观察的。

2.2 复合赋值符
在写代码时,我们经常可能对一个数进行自增、自减的操作,如下代码:

inta=10;a=a+3;a=a-2;

这样的代码C语言给提供了更加方便的写法:

inta=10;a+=3;a-=2;

Mii觉得a += 3这种写法不光短,而且一眼就能看出来是对a自己动手,比a = a + 3少写一遍a,也少了一次手滑写错的机会٩(๑˃̵ᴗ˂̵)۶

C语言中提供了复合赋值符,方便我们编写代码,这些赋值符有:

+=-=*=/=%=//下面的操作符后期讲解>>=<<=&=|=^=

3.单目操作符:++、–、+、-
前面介绍的操作符都是双目操作符,有2个操作数的。C语言中还有一些操作符只有一个操作数,被称为单目操作符。++、–、+(正)、-(负)就是单目操作符的。

3.1 ++和–
++是一种自增的操作符,又分为前置++和后置++。

🎈 前置++,后置++都是让它的操作数自增1,区别在于表达式的值不同。

–是一种自减的操作符,也分为前置–和后置–。

🎈 前置–,后置–都是让它的操作数自减1,区别在于表达式的值不同。

3.1.1 前置++

inta=10;intb=++a;//++的操作数是a,是放在a的前面的,就是前置++printf("a=%d b=%d\n",a,b);

计算口诀:先+1,后使用

a原来是10,先+1,后a变成了11,再使用就是赋值给b,b得到的也是11,所以计算结束后,a和b都是11,相当于这样的代码:

inta=10;a=a+1;b=a;printf("a=%d b=%d\n",a,b);

3.1.2 后置++

inta=10;intb=a++;//++的操作数是a,是放在a的后面的,就是后置++printf("a=%d b=%d\n",a,b);

计算口诀:先使用,后+1

a原来是10,先使用,就是先赋值给b,b得到了10,然后再+1,然后a变成了11,所以结束后a是11,b是10,相当于这样的代码:

inta=10;intb=a;a=a+1;printf("a=%d b=%d\n",a,b);

你看,两段代码就差了一个++的位置,一个写在前面一个写在后面,b拿到的值就从11变成了10,Mii第一次看的时候还愣了一下(´・ω・`?)

3.1.3 前置–
如果你听懂了前置++,那前置–是同理的,只是把加1,换成了减1;

计算口诀:先-1,后使用

inta=10;intb=--a;//--的操作数是a,是放在a的前面的,就是前置--printf("a=%d b=%d\n",a,b);//输出的结果是:9 9

3.1.4 后置–
同理后置–类似于后置++,只是把加1换成了减1。

计算口诀:先使用,后-1

inta=10;intb=a--;//--的操作数是a,是放在a的后面的,就是后置--printf("a=%d b=%d\n",a,b);//输出的结果是:9 10

这四个Mii整理成了一个小表格,方便以后忘了的时候回来瞄一眼(假设一开始a都是10):

写法口诀执行后a表达式本身的值
++a先+1,后使用1111
a++先使用,后+11110
--a先-1,后使用99
a--先使用,后-1910

这里要特别注意:不管前置还是后置,a自己是肯定会变的,变的只是“表达式本身的值”,也就是赋值给b的那个值,别把这两个搞混了(๑•̀ㅂ•́)و✧

3.2 +和-
这里的+是正号,-是负号,都是单目操作符。

运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。

inta=+10;//等价于 int a = 10;

运算符-用来改变一个值的正负号,负数的前面加上-就会得到正数,正数的前面加上-会得到负数。

inta=10;intb=-a;intc=-10;printf("b=%d c=%d\n",b,c);//这里的b和c都是-10
inta=-10;intb=-a;printf("b=%d\n",b);//这里的b是10

4.表达式求值
学会了上述操作符后,我们使用操作符可以写出一些更加复杂的表达式,比如:

3+4*5;5*6/2;

那这些表达式的计算顺序是啥?这里就得了解学习一下操作符的属性。

操作符的属性有:优先级和结合性;这两个属性决定了表达式求值的计算顺序。

4.1 操作符的属性

操作符属性:https://zh.cppreference.com/w/c/language/operator_precedence
这张表Mii第一次看到的时候头皮都麻了,这么多操作符怎么可能记得住(;﹏;)。其实不用全背下来,常用的优先级记住,剩下的用的时候回来查表就行了~

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

3+4*5;

上面示例中,表达式3 + 4 * 5里面既有加法运算符(+),又有乘法运算符(*)。由于乘法的优先级高于加法,所以会先计算4 * 5,而不是先计算3 + 4。

运算符的优先级顺序很多,下面是部分运算符的优先级顺序(按照优先级从高到低排列),建议大概记住这些操作符的优先级就行,其他操作符在使用的时候查看上面表格就可以了。

  1. 圆括号(())
  2. 自增运算符(++),自减运算符(–)
  3. 单目运算符(+和-)
  4. 乘法(*),除法(/)
  5. 加法(+),减法(-)
  6. 关系运算符(<、>等)
  7. 赋值运算符(=)

由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。

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

5*6/2;

上面示例中,*和/的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算5 * 6,再计算/ 2。

优先级解决的是“谁先算”,结合性解决的是“优先级一样时往哪边算”,这两个是配套使用的,缺一不可哦(`・ω・´)

4.2 类型转换
在进行表达式求值的时候,有时候会涉及到数据类型的转换,类型转换一般分为:隐式类型转换和显示类型转换。

隐式类型转换,又分为整型提升和常规算术转换,显示类型转换就是强制类型转换了。

隐式类型转换(又称自动类型转换)由编译器自动完成,无需程序员显式写出转换代码。它的目的是使表达式中的操作数类型兼容。

整型提升:表达式中的char、short等长度小于整型类型的长度,在表达式计算的过程中会被自动提升为整型类型(int或unsigned int),然后参与运算,这里的类型提升就是整型提升。

charc=100;shorts=2000;inti=c+s;//c和s都被提升为int后再进行加法运算

更精确地说(C11 §6.3.1.1):

  1. 参与运算的类型:char、signed char、unsigned char、short、unsigned short、int位段、枚举类型
  2. 判定条件:若int的取值范围能覆盖原类型的所有值 → 提升为int;否则 → 提升为unsigned int
  3. 提升后的类型:始终是int或unsigned int(不会提升为long等更大的类型)
  4. 保留值不变:提升后的值与原值相等

常规算术转换:变量的数据类型的长度是大于等于整型类型的长度时,如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。

intn=10;floatf=5.5f;floatr=n+f;//n会转换为float类型,再和f进行加法运算

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

longdoubledoublefloatunsignedlongintlongintunsignedintint

这个列表是从“大”到“小”排的,越靠下越小,小类型要往大类型靠,这样才不会丢数据(´・ω・`)

强制类型转换:C语言的隐式类型转换都是按照既定的规则来转换的,但是有时候我们需要更大程度上控制类型的转换,所以就提供了强制类型转换。

形式如下:

(类型)表达式

代码样例:

#include<stdio.h>intmain(){intn=3.14;//有警告intm=(int)3.14;//将3.14强制类型转换为int类型inta=5;intb=2;floatc=(float)a/b;//这里如果不强制类型转换就是整数除法的printf("%f\n",c);return0;}

4.3 问题表达式
C语言中没有规定子表达式的计算顺序,如果一个表达式比较复杂,包含了多个子表达式,那么子表达式的计算顺序不确定的情况下就会影响表达式的求值结果。比如:

(a+b)*(c-d)

这个表达式无法确定a + b和c - d谁先计算。大部分情况下,a + b和c - d谁先算不影响结果。但是如果子表达式中改变了某个操作数的值的时候,产生的值可能就不一致了。比如:

a=10;c=(b=a+3)-(a=5);//结果是未定义的

如果先执行b = a+3,则c = 8,如果先执行a = 5,则c = 3。这类包含了子表达式的表达式,子表达式的计算顺序就可能引发一些未定义行为,这就是问题表达式。

1.问题表达式1

#include<stdio.h>intmain(){inti=1;intr=(++i)+(++i)+(++i);printf("%d\n",r);return0;}

这段代码在不同的编译器上算的结果是不一样的,这就是问题表达式。为什么呢?

VS2019的运行结果:

mingw64-gcc的运行结果:

同一个表达式,VS2026算出来是12,mingw64-gcc算出来是10,Mii反复确认了好几遍才相信这是同一段代码(;﹏;)

原因就在于:C语言标准只规定了操作符的优先级和结合性,并没有规定子表达式的计算顺序。像(++i)这种既要取值、又要修改i的子表达式,到底谁先算、什么时候真正把i加一,标准把决定权交给了编译器,所以编译器想怎么算就怎么算,这就叫未定义行为。

2.问题表达式2

#include<stdio.h>intmain(){inti=10;i=i-----i*(i=-3)*i+++++i;printf("i = %d\n",i);return0;}

这段代码来自《C和指针》这本书,书的作者测试了下面这么多的编译器,程序运行结果各不相同。

一个表达式居然能算出从-128到42这么多种结果,Mii看到这张表的时候是真的震惊了(゚д゚),原来“能算出结果”和“结果是唯一的”完全是两回事

所以我们以后在编写表达式的时候,尽量简化表达式,让表达式能够有唯一的计算路径,这样的代码才是可靠的。

到这里,第3讲常见操作符和表达式求值Mii就全部整理完啦。回头看看,C语言的表达式看着简单,坑是真的不少:整数除法和浮点数除法差一个.0,求模的正负号跟着第一个操作数走,++写在前面和后面结果不一样,还有那种一个表达式里改好几次同一个变量的写法干脆就是未定义行为(;﹏;)。所以Mii给自己定了个小规矩:表达式宁可多写几行、拆开写,也不要为了省事写出看不懂的代码,毕竟能看懂、结果唯一的代码才是好代码嘛(๑•̀ㅂ•́)و✧

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 2:17:10

继承和接口(Java)

继承继承是一种由已有的类创建新类的机制。利用继承可以先定义一个共有属性的一般类&#xff0c;根据该一般类再定义具有特殊属性的子类&#xff0c;子类继承一般类的属性和行为&#xff0c;并根据需要增加它自己的新的属性和行为。特点&#xff1a;java不支持多重继承&#xf…

作者头像 李华
网站建设 2026/10/31 2:16:26

山区落石目标检测数据集 助力边坡落石风险智能监测 AI深度学习第10837期

文章目录山区落石目标检测数据集&#xff0c;助力边坡落石风险智能监测数据概括应用价值小白入手训练代码山区落石目标检测数据集&#xff0c;助力边坡落石风险智能监测 边坡落石检测是山区道路地质安防的重要方向&#xff0c;这份山区落石目标检测数据集&#xff0c;面向边坡…

作者头像 李华
网站建设 2026/10/31 2:15:38

Claude 桌面端已经更新,为什么还报错?我把两个问题一起修好了

我明明已经下载了新版 Claude 桌面端&#xff0c;发一句问候&#xff0c;界面却只给我一个 Invalid request。 排查之后才发现&#xff0c;桌面端实际启动的 Claude Code 还是 2.1.237&#xff0c;而当前网关要求的最低版本是 2.1.280。修好这一层&#xff0c;界面又提示 Haiku…

作者头像 李华
网站建设 2026/10/31 2:14:53

Day07 Python数据存储与运算实战全解

Day07 | Python数据存储与运算技术栈全解&#xff1a;从内存模型到ATM取款机实战摘要&#xff1a;本文是《AI应用开发筑基》系列的第一篇技术笔记&#xff0c;聚焦Python编程中最底层的两块基石——数据存储&#xff08;字面量、变量、五大基本类型、内存模型&#xff09;与数据…

作者头像 李华
网站建设 2026/10/31 2:14:53

做方案要街景图?这个工具能下8K原始无水印街景图

做方案、跑外业、搭全景项目的人&#xff0c;大概率都遇到过同一个问题&#xff1a;地图里明明能看到街景&#xff0c;却拿不下来。截图放大就糊&#xff0c;四周还被浏览器窗口切掉&#xff0c;放进文档里一眼就是凑数的。 下面这个工具就是为这件事做的&#xff0c;只需要街…

作者头像 李华