【C语言】——详解操作符(上)
【C语言】——详解操作符
- 一、 操作符的分类
- 二、 算数操作符
- 2.1、 + + + 、 − - − 和 ∗ * ∗
- 2.2、 / / / 操作符
- 2.3、 % 操作符
- 三、 位移操作符
- 3.1、 原码、 反码、 补码
- (1)原码
- (2)反码
- (3)补码
- 3.2、 左移操作符
- 四、 位操作符
- 五、 赋值操作符
- 5.1、 = = = 操作符
- 5.2、 复合赋值操作符
- 六、 单目操作符
- 6.1、 ++ 与 -- --
- (1)前置++
- (2)后置++
- (3) -- --
- 6.2、 + 和 --
- 6.3、 强制类型转换
- 6.4、sizeof
- 6.5、!
一、 操作符的分类
- 算数操作符:+ 、 - 、* 、/ 、%
- 位移操作符:>
- 位操作符: & 、 | 、^ 、 ~
- 赋值操作符:= 、 += 、 -= 、 *= 、 /= 、 %= 、 = 、 &= 、 |= 、 ^=
- 单目操作符: ! 、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、(类型)
- 关系操作符:> 、 >= 、
二、 算数操作符
+ + +、 − - − 、 ∗ * ∗ 、/ 、%
我们在写代码时,一定会涉及运算。
C语言中,为了方便运算,提供了算术操作符,他们都是双目操作符。
2.1、 + + + 、 − - − 和 ∗ * ∗
+ + + 、 − - − 以及 ∗ * ∗ 是用来完成加法、减法和乘法的.
他们都有两个操作数,位于操作符两端的就是他们的操作数。因此,这种操作符也叫做双目操作符。
代码示例:
#include int main() { int a = 3, b = 5; int add = a + b; int sub = a - b; int mul = a * b; printf("%d + %d = %d\n", a, b, add); printf("%d - %d = %d\n", a, b, sub); printf("%d * %d = %d\n", a, b, mul); return 0; }
运行结果:
2.2、 / / / 操作符
说到C语言中的除法运算,我想先给大家看个例子:
#include int main() { int a = 5; int b = 2; float c = a / b; printf("%d / %d = %f\n", a, b, c); return 0; }
代码的运行结果是什么?会得到 2.5 2.5 2.5 吗?让我们一起来看看运行结果:
为什么会这样呢?明明存放结果的变量类型已经是浮点型 ( f l o a t ) (float) (float) 了呀,为什么小数部分还是 0 0 0 呢?
原因在于C语言中的整数除法是整除,只会返回整数部分,丢弃小数部分.
如果希望得到浮点数的结果,两个运算符必须至少有一个浮点数,这时 C 语言就会进行浮点数除法.
参考代码:
#include int main() { int a = 5; float b = 2; int c = a / b; printf("%d / %f = %d\n", a, b, c); return 0; }
运行结果:
哈哈哈,没想到吧!只有除数或被除数是浮点型 ( f l o a t ) (float) (float) 也是不行的哦,结果也是要浮点型 ( f l o a t ) (float) (float)
参考代码:
#include int main() { int a = 5; float b = 2; float c = a / b; printf("%d / %f = %f\n", a, b, c); return 0; }
运行结果:
2.3、 % 操作符
操作符 % 表示求模运算,即返回两个整数相除的余值。这个操作符只能用于整数,不能用于浮点数。
同时,负数求模的规则是:结果的正负号由第一个运算数的正负号决定
参考代码:
#include int main() { printf("%d\n", 11 % 5); printf("%d\n", 11 % -5); printf("%d\n", -11 % 5); printf("%d\n", -11 % -5); return 0; }
运行结果:
三、 位移操作符
左移操作符 : >>
注:位移操作符的操作数只能是整数。
3.1、 原码、 反码、 补码
位移操作符实质上是对数据的二进制位进行操作,因此了解整数的二进制表示方法至关重要。
整数的二进制表示方法三种:
- 原码
- 反码
- 补码
对于 ( s i g n e d ) i n t (signed )int (signed)int (有符号整型),它的三种表示方法均分为符号位和数值位两个部分。二进制序列中,最高位被当做符号位,剩余的都是数值位。
符号位都是用 0 0 0 表示正,用 1 1 1 表示负。
- 正整数的原、 反、 补码都相同
- 负整数的三种表示方法各不相同
(1)原码
直接将数值按照正负数的形式翻译成二进制得到的就是原码。
示例一:
#include int main() { int a = -10; return 0; }
- -10是存放在 a a a 中, a a a 是整型变量,有4个字节,即32个bit位
- 10的二进制为:1010
- -10为负数,负数用最高位 1 来表示
- 因为一共4个字节,中间位用 0 补为位
(2)反码
将原码的符号位不变,其他位依次按位去反,即反码
(3)补码
反码 +1 就得到补码
示例二:
#include int main() { int a = 10; return 0; }
而对于正数来说,它的三种表示方法相同:
补码得到原码:
- − 1 -1 −1 ,取反
- 取反,
+
1
+1
+1
对于整型来说:数据存放内存中都是用补码进行运算和存储
原因在于,使用补码,可以将符号位和数值位统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码相互转换,其运算过程是相同的(取反, + 1 +1 +1),不需要额外的硬件电路。
3.2、 左移操作符
p /pp 移位规则:左边抛弃、右边补 0/pp /ppimg src="https://img-blog.csdnimg.cn/direct/2ce8be30ad2a40fcabf8b42579905737.gif" //ppem注:调试时,在需要观测的整型变量后面加上 “ font color="red" , b ,b ,b /font”,即为其二进制表示。/em/pp /pp运行结果:/ppimg src="https://img-blog.csdnimg.cn/direct/83856acfe5ef4bc5950855e6841ad877.png" //pp图示:/p blockquote pimg src="https://img-blog.csdnimg.cn/direct/5727c965bf9d45eeb016d772370871f9.gif" //p /blockquote p /p h43.3、 右移操作符 >移位规则:首先右移运算分为两种:
- 逻辑右移:左边用 0 0 0 填充,右边丢弃
- 算术右移:左边用原该值的符号位填充,右边丢弃
注:大部分编译器都是算术右移。
运行结果:
图示:
注:对于移位操作符,不要移动负数位,这个标准是未定义的!
int a = 10; a >> -1//error
四、 位操作符
位操作符有:
& 按位与
| 按位或
^ 按位异或
~ 按位取反
注:他们的操作数必须是整数,且操作的都是二进制位
(1)& 按位与
逻辑:有 0 为 0,全 1 为 1
运行结果:
图示:
(2)| 按位或
逻辑:有 1 为 1,全 0 为 0
运行结果:
图示:
(3)^ 按位异或
逻辑:相同为 0,相异为 1
运行结果:
图示:
(3)~ 按位取反
运算结果:
五、 赋值操作符
= 、 += 、 -= 、 *= 、 /= 、 %= 、 = 、 &= 、 |= 、 ^=
5.1、 = = = 操作符
在创建变量的时候给一个值初始值叫初始化,在变量创建好后,再给一个值,这叫赋值。
int a = 0;//初始化 a = 200;//赋值,这里使用的就是赋值操作符
赋值操作符 = = = 是一个随时可以给变量赋值的操作符。
赋值操作符也可以连续赋值,如:
int a = 3; int b = 5; int c = 0; c = b = a + 5;//连续赋值,从右向左依次赋值
注:C语言虽然支持这种连续赋值,但是写出的代码不易理解,建议还是拆开来写,这样方便观测代码的执行细节。
5.2、 复合赋值操作符
在写代码时,我们可能经常对一个数自增、自减的操作:
int a = 10; a = a + 3; a = a - 2;
这样代码C语言提供了更加方便的方法:
int a = 10; a += 3; a -= 2;
C语言中提供了复合赋值符,方便我们编写代码,这些附值符有:
+= 、 -= 、 *= 、 /= 、 %= 、 = 、 &= 、 |= 、 ^=
六、 单目操作符
! 、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、(类型)
在C语言中,有些操作符只有一个操作数,他们被称为单目操作符。
6.1、 ++ 与 – –
++是一种自增操作符,分为前置++和后置++,– –是一种自减操作符,也分为前置– –和后置 – –
(1)前置++
#include int main() { int a = 10; int b = ++a; printf("%d %d\n", a, b); return 0; }
运行结果:
这段代码中, a a a 原来是 10 ,先 +1 , a a a 变为11,再使用就是赋值给 b b b , b b b 得到的也是11,所以 a a a 和 b b b 都是 11。
计算口诀:先 +1,后使用
相当于以下代码:
#include int main() { int a = 10; a = a + 1; int b = a; printf("%d %d\n", a, b); return 0; }
(2)后置++
#include int main() { int a = 10; int b = a++; printf("%d %d\n", a, b); return 0; }
运行结果:
上述代码中, a a a 原来是10,先使用,就是先赋值给 b b b , b b b 得到了10,然后 a a a 自增成了11,所以运行结果 a a a 是11, b b b 是10.
计算口诀:先使用,后 +1
相当于如下代码:
#include int main() { int a = 10; int b = a; a = a + 1; printf("%d %d\n", a, b); return 0; }
(3) – –
– – 操作符和 ++ 原理一样,只是自增变成自减而已,这里就不再过多赘述了,下面给段代码大家一起来检验一下吧
#include int main() { int a = 10; int b = a--; int c = --a; printf("%d %d %d\n", a, b, c); return 0; }
运行结果:
怎么样,你做对了吗?
6.2、 + 和 –
这里的 + 是正号, – 是负号,他们都是单目操作符。
操作符 + 对正负值没有影响,是一个完全可以省略的操作符,但是写了也不会报错
#include int main() { int a = 10; int b = +10; printf("%d %d\n", a, b); return 0; }
操作符 – 用来改变一个正值的正负号,在负数的前面加上 – 就会得到正数,在正数前面加上 – 就会得到负数。
#include int main() { int a = 10; int b = -a; int c = -10; printf("b = %d c = %d\n", b, c); int aa = -10; int bb = -aa; printf("bb = %d\n", bb); return 0; }
6.3、 强制类型转换
在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,如下:
(类型)
请看代码:
#include int main() { int a = 3.14; printf("%d/n", a); return 0; }
为了消除这个警告,我们可以使用强制类型转换:
#include int main() { int a = (int)3.14; printf("%d\n", a); return 0; }
但,俗话说:强扭的瓜不甜,我们使用强制类型转换都是万不得已的时候使用,如果不需要强制类型转换就能实现代码,这样自然最好。
6.4、sizeof
看到这,是不是很惊讶呢,没错! s i z e o f sizeof sizeof 并不是函数,而是一个操作符
s i z e o f sizeof sizeof 是一个关键字,也是一个操作符,是专门用来计算类型长度的,单位是字节
s i z e o f sizeof sizeof 是单目操作符,操作数可以是类型,也可以是变量或者表达式。
sizeof(类型) sizeof 表达式
- s i z e o f sizeof sizeof 的操作数如果不是类型,是表达式的时候,可以省略后边的括号的。
- s i z e o f sizeof sizeof 后边的表达式是不真实参与运算的,根据表达式的类型雷得出大小。
-
s
i
z
e
o
f
sizeof
sizeof 的计算结果是
s
i
z
e
size
size_
t
t
t 类型的。
s i z e o f sizeof sizeof 操作符的返回值,C语言只规定是无符号整数,并没有规定具体类型,而是留给系统自己决定。 s i z e o f sizeof sizeof 到底返回什么类型,在不同的系统中,返回值的类型有可能是 u n s i g n e d unsigned unsigned i n t int int ,也有可能是 u n s i g n e d unsigned unsigned l o n g long long,甚至是 u n s i g n e d unsigned unsigned l o n g long long l o n g long long。他们对应的 p r i n t f () printf() printf() 占位符分别是 % u u u、% l u lu lu和 % l l u llu llu。这样不利于程序的可移植性。
C语言提供了一个解决方法,创造了一个类型别名 s i z e size size_ t t t,用来统一表示 s i z e o f sizeof sizeof 的返回值类型。对应当前系统的 s i z e o f sizeof sizeof 的返回值类型,可能是 u n s i g n e d unsigned unsigned i n t int int ,也可能是 u n s i g n e d unsigned unsigned l o n g long long l o n g long long。
比如:
#include int main() { int a = 10; printf("%zd\n", sizeof(a)); printf("%zd\n", sizeof a);//a是变量的名字,可以省略sizeof后面的() printf("%zd\n", sizeof(int)); printf("%zd\n", sizeof(3 + 3.5)); return 0; }
s i z e o f sizeof sizeof中表达式不计算 !
#include int main() { short s = 2; int b = 10; printf("%d\n", sizeof(s = b + 1)); printf("s = %d\n", s); return 0; }
s i z e o f sizeof sizeof 在进行编译的时候,就根据表达式的类型确定了,而表达式的执行却要在程序运行期间才能执行,在编译期间已经将 s i z e o f sizeof sizeof 处理掉了,所以在运行期间就不会再执行表达式了。
6.5、!
在C语言中:0 为假,非 0 为真(负数也为真)
! 操作符作用是:逻辑取反
图示:
比如,我们有一个变量 f l a g flag flag,如果 f l a g flag flag 为假,要做一个什么事情,就可以这样写代码:
#include int main() { int flag = 0; if (!flag) { printf("do something\n"); } return 0; }
如果 f l a g flag flag 为真, ! f l a g !flag !flag 就是假,如果 f l a g flag flag 为假, ! f l a g !flag !flag 就是真
所以上面的代码意思是 f l a g flag flag 为假,执行 i f if if 语句中的代码.
剩下的两个单目操作符 & 和 ∗ * ∗,将在之后指针内容一起介绍,敬请期待!