行业知识
C语言:操作符详解
��
创作不易,给个三连吧!!
一、算术操作符
C语言中为了方便计算,提供了算数操作符,分别是:+,-,*,/,%
由于这些操作符都是有两个操作数(位于操作符两边),所以这种操作符也叫做双目操作符。
1.1 +,-,*操作符
+操作符用于两数相加
-操作符用于两数相减
*操作符用于两数相乘
前三个操作符都不容易出错,下面重点介绍/和%操作符
1.2 / 操作符
/ 操作符用于两数相除
1,除号的两端如果是整数,执行的是整数除法,得到的也一定是整数!
int main(){ float x = 6 / 4; int y = 6 / 4; printf("%f\n", x); // 输出 1.000000 printf("%d\n", y); // 输出 1 return 0;} 上述示例中,尽管x是float类型,但由于C语言的整数除法是整除,只会返回整数部分,并丢弃小数部分,所以6/4得到的结果是1.0而不是1.5
2,如果我们希望两个整数相除得到浮点数的结果,那么两个运算数至少得有一个浮点数 !
int main(){ float x = 6.0 / 4;//也可写成6/4.0 printf("%f\n", x); // 输出 1.500000 return 0;} 由于两个操作数一个是int类型,一个是float类型,根据寻常算数转换体系,int要转成float参与计算,因此6.0/4(或者6/4.0)得到的结果就是1.5
1.3 %操作符
%操作符用于进行求模运算,即返回两个整数相除的余值,该操作符只能用于整数!!
注:求模的规则是,结果的正负号由第一个运算数的正负号决定
int main(){ printf("%d\n", 11 % -5); // 1 printf("%d\n",-11 % -5); // -1 printf("%d\n",-11 % 5); // -1 return 0;} 碰到正负数求模或者纯负数求模,先把负号忽略不看,计算完后再根据第一个运算数给符号
二、赋值操作符
在变量创建的时候给⼀个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。
int a = 100;//初始化a = 200;//赋值,这⾥使⽤的就是赋值操作符
2.1 连续赋值
赋值操作符也可以连续赋值,如:
int a = 3;int b = 5;int c = 0;c = b = a+3;//连续赋值,从右向左依次赋值的。
赋值是从右往左依次赋值的!!!
C语⾔虽然⽀持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样⽅便观察代码的 执⾏细节。
2.2 复合赋值符
在写代码时,我们经常可能对⼀个数进⾏⾃增、⾃减的操作,如下代码:
int a = 10;a = a+3;a = a-2;
这样代码C语⾔给提供了更加⽅便的写法:
int a = 10;a += 3;a -= 2;
所有的复合赋值符:
+= -= *= /= %= >>= = ⼤于等于运算符12 返回 1 , 12 > 20 返回 0 。 关系表达式常⽤于 if 或 while 结构。
if (x == 3) { printf("x is 3.\n");}2,==于=是两个不一样的运算符,如果误用会造成严重后果!!
int a=0;if(a=3)printf("hehe");如上述代码,我们本来想表达的是如果a==3就打印hehe,按道理不应该打印出hehe,但因为写成了a=3,该条件始终为真,所以一定会打印出hehe!!
为了防止这个错误,我们尽量将变量写在等号右边,这样的话如果我们不小心把==写成=了,编译器会报错提醒你!!
/* 报错 */if (3 = x) ...3,多个关系运算符不宜连用
比如:
i我们想表达的是i小于j且j小于k,但是由于关系运算符是从左往右运算的,所以相当于
(iib ? a : b; printf("%d\n", m); return 0;}
六、逻辑操作符
逻辑运算符提供逻辑判断功能,⽤于构建更复杂的表达式,主要有下⾯三个运算符。
! :逻辑取反运算符(改变单个表达式的真假)。
&& :与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。
|| :或运算符,就是或者的意思(两侧⾄少有⼀个表达式为真,则为真,否则为假)。
注:C语⾔中,⾮0表⽰真,0表⽰假
6.1 逻辑取反运算符
⽐如,我们有⼀个变量叫 flag ,如果flag为假,要做⼀个什么事情,就可以这样写代码:
int main(){ int flag = 0; if(!flag) { printf("do something\n"); } return 0;}如果 flag 为真, !flag 就是假,如果 flag 为假, !flag 就是真
所以上⾯的代码的意思就是 flag 为假,执⾏if语句中的代码。
6.2 与运算符
&& 就是与运算符,也是并且的意思, && 是⼀个双⽬操作符,使⽤的⽅式是 a&&b , && 两边的表达 式都是真的时候,整个表达式才为真,只要有⼀个是假,则整个表达式为假。
⽐如:如果我们说⽉份是3⽉到5⽉,是春天,我们可以这样写代码
int month = 0;scanf("%d", &month);if(month >= 3 && month = 3 && month = 3 ,右操作数是 month = 3 的 结果是0的时候,即使不判断 month b, a=b+10, a, b=a+1);//逗号表达式c是多少?a>b为假,所以返回0,但是不影响a也不影响b
a=b+10得a=12
单个a什么也没影响
b=a+1得b=13
c接收最后一个表达式得结果,所以输出13
代码2:
//代码2if (a =b + 1, c=a / 2, d > 0)不管前面怎么计算,取决于最后得表达式d>0,因为前面的表达式都跟d没关系,所以可以忽略,这个判断条件就是d是否>0
因此我们可以得到结论:逗号表达式的结果一般就看最后一个表达式的结果,但是要注意前面的表达式是否会影响最后一个表达式,如果会影响就要一个个从左往右推过来,如果不会影响就直接忽略不看就行
逗号表达式的应用:
a= get_val();count_val(a);while (a > 0){ //业务处理 a = get_val(); count_val(a);}我们发现while循环之前的代码和while循环里面的代码有点冗余了,所以我们可以用逗号表达式来修改一下,使其不冗余
while (a = get_val(), count_val(a), a>0){ //业务处理;}八、函数调用操作符
在博主有关函数的文章里有介绍
C语言:函数-CSDN博客
九、下标引用操作符
在博主有关指针的文章里有介绍
C语言:深入理解指针(2)-CSDN博客
十、结构体成员访问操作符
在博主的有关结构体的文章有介绍
C语言:自定义类型——结构体-CSDN博客
十一、移位操作符和位操作符
在博主有关二进制的文章里有介绍
C语言:进制转换以及原码、反码、补码_原码右移规则-CSDN博客
十二、操作符的属性:优先级和结合性
C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
12.1 优先级
优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是 不⼀样的。(相邻操作符,优先级高的先执行,优先级低的后执行)
3 + 4 * 5;上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法 的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。
12.2 结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符 是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右 结合(从右到左执⾏),⽐如赋值运算符( = )。
5 * 6 / 2;上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 , 再计算 6 / 2
12.3 总结
1,运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概记住这些操作符的优先级就⾏
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( 等)
• 赋值运算符( = )
其他操作符在使⽤的时候查看下⾯表格就可以了。
参考:https://zh.cppreference.com/w/c/language/operator_precedence
十三、表达式求值
14.1 整型提升和算数转化
在博主有关数据在内存种存储形式的文章里有介绍
C语言:数据在内存中的存储形式-CSDN博客
14.2 问题表达式解析
表达式1:
//表达式的求值部分由操作符的优先级决定。//表达式1a*b + c*d + e*f关于优先级,我们只能保证相邻操作符的优先级是*比+高,而不能保证第三个*比第一个+早执行,因为不相邻所以表达式的计算机顺序可能是:
a*bc*da*b + c*de*fa*b + c*d + e*f也可能是
a*bc*de*fa*b + c*da*b + c*d + e*f可能你会觉得,这两个表达式不管怎么算结果都是一样的,如果abcdef都仅仅只是一个变量,确实是这样的,但如果abcdef并不是变量而是表达式,那么计算的先后顺序可能就会对表达式的结果有影响!所以我们要尽量避开这些有歧义的写法!! (尽量拆开写)
表达式2:
//表达式2c + --c;同上,操作符的优先级只能决定⾃减 -- 的运算在 + 的运算的前⾯,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。(两个变量是相同的,右边的变量计算后可能会影响到左边,所以也要尽量拆开写)
表达式3:
//表达式3int main(){ int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0;}该表达式出自书籍《C和指针》,作者在不变编译器中的测试结果是不一样的!!
因此我们应该避开写这种复杂而又难以理解的代码!!
表达式4:
int fun(){ static int count = 1; return ++count;}int main(){ int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//输出多少? return 0;}这个代码有没有实际的问题?有问题!
虽然在⼤多数的编译器上求得结果都是相同的。
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。
函数的调⽤先后顺序⽆法通过操作符的优先级确定。
所以上述代码的结果可能是2-3*4=-10,也可能是4-2*3=-2
表达式5:
//表达式5#include int main(){ int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0;}在vs2022上,运行结果是4 12
在gcc编译器上,运行结果是4 10
这个和表达式1的情况是一样的,因为我们只能确定相邻操作符的优先级,即++优先级高于+,但是我们无法确定第1个+和第3个++的优先级谁高。
在vs2022中,计算顺序是这样的:
++i++i++i//i变成4了ret=4+4+4=12在gcc中,计算顺序是这样的:
++i++i//此时i=3先把前两个加在一起:3+3=6第三个++i//此时i=4ret=6+4=10所以我们要尽量避开这些有歧义的代码,因为在不同编译器底下可能是存在差异的,尽量不要写这种复杂代码,要尽量拆开写!!
总结:
即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的计算路径,那这个表达式就是存在潜在⻛险的(不同的编译器可能存在差异),建议不要写出特别负责的表达式。必要性可以拆开来写,或者加上括号,来明确计算顺序!!!
上一篇:电脑主机开不开闪红灯怎么办视频