当前位置: 首页 > article >正文

5.运算符

文章目录

  • 五、运算符
    • 1.算术运算符
    • 2.自增运算符,自减运算符
    • 3.关系运算符
    • 4.逻辑运算符
    • 5.位运算符
    • 6.逗号运算符
    • 7.运算优先级


提示:以下是本篇文章正文内容,下面案例可供参考

五、运算符

C 语言的运算符非常多,一共有 50 多种,可以分成若干类。

1.算术运算符

算术运算符专门用于算术运算,主要有下面几种。
+:正值运算符(一元运算符)
-:负值运算符(一元运算符)
+:加法运算符(二元运算符)
-:减法运算符(二元运算符)
*:乘法运算符
/ :除法运算符
% :余值运算符
(1) + , -
+和 - 既可以作为一元运算符,也可以作为二元运算符。所谓“一元运算符”,指的是只需要一个运算数就
可以执行。一元运算符 - 用来改变一个值的正负号。

int x = -12;

上面示例中, - 将 12 这个值变成 -12 。
一元运算符 + 对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。

int x = -12;
int y = +x;

上面示例中,变量 y 的值还是 -12 ,因为 + 不会改变正负值。
二元运算符 + 和 - 用来完成加法和减法。

int x = 4 + 22;
int y = 61 - 23;

(2) *
运算符 * 用来完成乘法。

int num = 5;
printf("%i\n", num * num); // 输出 25

(3) /
运算符 / 用来完成除法。注意,两个整数相除,得到还是一个整数。

float x = 6 / 4;
printf("%f\n", x); // 输出 1.000000

上面示例中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,而不是 1.5 。原
因就在于 C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。
如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时 C 语言就会进行浮点数除法。

float x = 6.0 / 4; // 或者写成 6 / 4.0
printf("%f\n", x); // 输出 1.500000

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

int score = 5;
score = (score / 20) * 100;

上面的代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为 score /
20 是整除,会得到一个整数值 0 ,所以乘以 100 后得到的也是 0 。
为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。

score = (score / 20.0) * 100;

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

int x = 6 % 4; // 2

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

11 % -5 // 1
-11 % -5 // -1
-11 % 5 // -1

上面示例中,第一个运算数的正负号( 11 或 -11 )决定了结果的正负号。
(5)赋值运算的简写形式
如果变量对自身的值进行算术运算,C 语言提供了简写形式,允许将赋值运算符和算术运算符结合成一
个运算符。
+=
-=
*=
/=
%=
下面是一些例子。

i += 3;  // 等同于 i = i + 3
i -= 8;  // 等同于 i = i - 8
i *= 9;  // 等同于 i = i * 9
i /= 2;  // 等同于 i = i / 2
i %= 5;  // 等同于 i = i % 5

2.自增运算符,自减运算符

C 语言提供两个运算符,对变量自身进行 + 1 和 - 1 的操作。
++ :自增运算符
– :自减运算符

i++; // 等同于 i = i + 1
i--; // 等同于 i = i - 1

这两个运算符放在变量的前面或后面,结果是不一样的。 ++var 和 --var 是先执行自增或自减操作,再
返回操作后 var 的值; var++ 和 var-- 则是先返回操作前 var 的值,再执行自增或自减操作。

int i = 42;
int j;
j = (i++ + 10);
// i: 43
// j: 52
j = (++i + 10)
// i: 44
// j: 54

上面示例中,自增运算符的位置差异,会导致变量 j 得到不同的值。这样的写法很容易出现意料之外的
结果,为了消除意外,可以改用下面的写法。

/* 写法一 */
j = (i + 10);
i++;
/* 写法二 */
i++;
j = (i + 10);

上面示例中,变量 i 的自增运算与返回值是分离的两个步骤,这样就不太会出错,也提高了代码的可读
性。

3.关系运算符

C 语言用于比较的表达式,称为“关系表达式”(relational expression),里面使用的运算符就称为“关
系运算符”(relational operator),主要有下面6个。

大于运算符
<小于运算符
=大于等于运算符
<=小于等于运算符
== 相等运算符
!=不相等运算符
下面是一些例子。

a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;

关系表达式通常返回 0 或 1 ,表示真伪。C 语言中, 0 表示伪,所有非零值表示真。比如, 20 > 12 返
回 1 , 12 > 20 返回 0 。

关系表达式常用于 ifwhile 结构。
if (x == 3) {
  printf("x is 3.\n");
}

注意,相等运算符 == 与赋值运算符 = 是两个不一样的运算符,不要混淆。有时候,可能会不小心写出下
面的代码,它可以运行,但很容易出现意料之外的结果。

if (x = 3) ...

上面示例中,原意是 x == 3 ,但是不小心写成 x = 3 。这个式子表示对变量 x 赋值 3 ,它的返回值为
3 ,所以 if 判断总是为真。
为了防止出现这种错误,有的程序员喜欢将变量写在等号的右边。

if (3 == x) ...

这样的话,如果把 == 误写成 = ,编译器就会报错。

/* 报错 */
if (3 = x) ...

另一个需要避免的错误是,多个关系运算符不宜连用。

i < j < k

上面示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果,即
不是保证变量 j 的值在 i 和 k 之间。因为关系运算符是从左到右计算,所以实际执行的是下面的表达
式。

(i < j) < k

上面式子中, i < j 返回 0 或 1 ,所以最终是 0 或 1 与变量 k 进行比较。如果想要判断变量 j 的值是否
在 i 和 k 之间,应该使用下面的写法。

i < j && j < k

4.逻辑运算符

逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。
! :否运算符(改变单个表达式的真伪)。
&& :与运算符(两侧的表达式都为真,则为真,否则为伪)。
|| :或运算符(两侧至少有一个表达式为真,则为真,否则为伪)。
下面是与运算符的例子。

if (x < 10 && y > 20)
  printf("Doing something!\n");

上面示例中,只有 x < 10 和 y > 20 同时为真, x < 10 && y > 20 才会为真。
下面是否运算符的例子。

if (!(x < 12))
  printf("x is not less than 12\n");

上面示例中,由于否运算符 ! 具有比 < 更高的优先级,所以必须使用括号,才能对表达式 x < 12 进行
否运算。当然,合理的写法是 if (x >= 12) ,这里只是为了举例。
对于逻辑运算符来说,任何非零值都表示真,零值表示伪。比如, 5 || 0 会返回 1 , 5 && 0 会返回
0 。
逻辑运算符还有一个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证
的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。

if (number != 0 && 12/number == 2)

上面示例中,如果 && 左侧的表达式( number != 0 )为伪,即 number 等于 0 时,右侧的表达式
( 12/number == 2 )是不会执行的。因为这时左侧表达式返回 0 ,整个 && 表达式肯定为伪,就直接返
回 0 ,不再执行右侧的表达式了。
由于逻辑运算符的执行顺序是先左后右,所以下面的代码是有问题的。

while ((x++ < 10) && (x + y < 20))

上面示例中,执行左侧表达式后,变量 x 的值就已经变了。等到执行右侧表达式的时候,是用新的值在
计算,这通常不是原始意图。

5.位运算符

C 语言提供一些位运算符,用来操作二进制位(bit)。
(1)取反运算符 ~
取反运算符 ~ 是一个一元运算符,用来将每一个二进制位变成相反值,即 0 变成 1 , 1 变成 0 。

// 返回 01101100
~ 10010011

上面示例中, ~ 对每个二进制位取反,就得到了一个新的值。
注意, ~ 运算符不会改变变量的值,只是返回一个新的值。
(2)与运算符 &
与运算符 & 将两个值的每一个二进制位进行比较,返回一个新的值。当两个二进制位都为 1 ,就返回
1 ,否则返回 0 。

// 返回 00010001
10010011 & 00111101

上面示例中,两个八位二进制数进行逐位比较,返回一个新的值。
与运算符 & 可以与赋值运算符 = 结合,简写成 &= 。

int val = 3;
val = val & 0377;
// 简写成
val &= 0377;

(3)或运算符 |
或运算符 | 将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位只要有一个为 1 (包
含两个都为 1 的情况),就返回 1 ,否则返回 0 。

// 返回 10111111
10010011 | 00111101

或运算符 | 可以与赋值运算符 = 结合,简写成 |= 。

int val = 3;
val = val | 0377;
// 简写为
val |= 0377;

(4)异或运算符 ^

异或运算符 ^ 将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位有且仅有一个为
1 ,就返回 1 ,否则返回 0 。

// 返回 10101110
10010011 ^ 00111101

异或运算符 ^ 可以与赋值运算符 = 结合,简写成 ^= 。

int val = 3;
val = val ^ 0377;
// 简写为
val ^= 0377;

(5)左移运算符 <<
左移运算符 << 将左侧运算数的每一位,向左移动指定的位数,尾部空出来的位置使用 0 填充。

// 1000101000
10001010 << 2

上面示例中, 10001010 的每一个二进制位,都向左侧移动了两位。
左移运算符相当于将运算数乘以2的指定次方,比如左移2位相当于乘以4(2的2次方)。
左移运算符 << 可以与赋值运算符 = 结合,简写成 <<= 。

int val = 1;
val = val << 2;
// 简写为
val <<= 2;

(6)右移运算符 >>
右移运算符 >> 将左侧运算数的每一位,向右移动指定的位数,尾部无法容纳的值将丢弃,头部空出来的
位置使用 0 填充。

// 返回 00100010
10001010 >> 2

上面示例中, 10001010 的每一个二进制位,都向右移动两位。最低的两位 10 被丢弃,头部多出来的两
位补 0 ,所以最后得到 00100010 。
注意,右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符
号位,有不同的做法,可能会得到不一样的结果。
右移运算符相当于将运算数除以2的指定次方,比如右移2位就相当于除以4(2的2次方)。
右移运算符 >> 可以与赋值运算符 = 结合,简写成 >>= 。

int val = 1;
val = val >> 2;
// 简写为
val >>= 2;

6.逗号运算符

逗号运算符用于将多个表达式写在一起,从左到右依次运行每个表达式。

x = 10, y = 20;

上面示例中,有两个表达式( x = 10 和 y = 20 ),逗号使得它们可以放在同一条语句里面。
逗号运算符返回最后一个表达式的值,作为整个语句的值。

int x;
x = 1, 2, 3;

上面示例中,逗号的优先级低于赋值运算符,所以先执行赋值运算,再执行逗号运算,变量 x 等于 1 。

7.运算优先级

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

3 + 4 * 5;

上面示例中,表达式 3 + 4 * 5 里面既有加法运算符( + ),又有乘法运算符( * )。由于乘法的优先
级高于加法,所以会先计算 4 * 5 ,而不是先计算 3 + 4 。
如果两个运算符优先级相同,则根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左
结合(从左到右执行),少数运算符是右结合(从右到左执行),比如赋值运算符( = )。

5 * 6 / 2;

上面示例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算 5 * 6 ,再计
算 6 / 2 。
运算符的优先级顺序很复杂。下面是部分运算符的优先级顺序(按照优先级从高到低排列)。
圆括号( () )
自增运算符( ++ ),自减运算符( – )
一元运算符( + 和 - )
乘法( * ),除法( / )
加法( + ),减法( - )
关系运算符( < 、 > 等)
赋值运算符( = )
由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。

int x = (3 + 4) * 5;

上面示例中,由于添加了圆括号,加法会先于乘法进行运算。
完全记住所有运算符的优先级没有必要,解决方法是多用圆括号,防止出现意料之外的情况,也有利于
提高代码的可读性。


http://www.kler.cn/a/7032.html

相关文章:

  • 模型的评估指标——IoU、混淆矩阵、Precision、Recall、P-R曲线、F1-score、mAP、AP、AUC-ROC
  • C/C++中使用MYSQL
  • Ubuntu 18.04 配置sources.list源文件(无法安全地用该源进行更新,所以默认禁用该源)
  • 另外一种缓冲式图片组件的用法
  • GIT将源码推送新分支
  • C++11(四)---可变参数模板
  • 10 Wifi网络的封装1
  • 首批因AI失业的人出现-某游戏公司裁掉半数原画师
  • 【Linux驱动基础详解】| Linux模块声明与描述
  • MATLAB字符串里怎么添加单引号
  • 逆向动力学算法(Python描述)
  • int * p、int * p 、int* p的区别及 指针*p的使用分析
  • Baklib支招:如何做好内部知识库?
  • 【从零开始学习 UVM】9.1、UVM Config DB —— UVM Resource database 资源库详解
  • UVM学习笔记2——验证基础知识(验证计划、验证方法)
  • 软件产品登记的材料
  • 一文总结 Shiro 实战教程
  • 金丹四层 —— 详解自定义类型
  • JAVA基础
  • 副词也可以做定语
  • 9.网络爬虫—MySQL基础
  • 中华好诗词(九)
  • linux系统编程(2)--Makefile
  • C++: Articles:Split a String
  • OpenResty+OpenWAF的WEB防护实战
  • Java程序设计——作业2循环