【C语言】——详解操作符

    • 一、 操作符的分类
    • 二、 算数操作符
      • 2.1、 ++ +−- ∗*
      • 2.2、 // / 操作符
      • 2.3、 % 操作符
    • 三、 位移操作符
      • 3.1、 原码、 反码、 补码
        • (1)原码
        • (2)反码
        • (3)补码
      • 3.2、 左移操作符 <<
      • 3.3、 右移操作符 >>
    • 四、 位操作符
    • 五、 赋值操作符
      • 5.1、 == = 操作符
      • 5.2、 复合赋值操作符
    • 六、 单目操作符
      • 6.1、 ++ 与 — —
        • (1)前置++
        • (2)后置++
        • (3) — —
      • 6.2、 + 和 —
      • 6.3、 强制类型转换
      • 6.4、sizeof
      • 6.5、!

一、 操作符的分类

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

二、 算数操作符

+++ −- ∗* 、/ 、%

  我们在写代码时,一定会涉及运算。

  C语言中,为了方便运算,提供了算术操作符,他们都是双目操作符

2.1、 +++ −- ∗*

   ++ +−- 以及 ∗* 是用来完成加法、减法和乘法的.
  他们都有两个操作数,位于操作符两端的就是他们的操作数。因此,这种操作符也叫做双目操作符

代码示例:

#includeint 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语言中的除法运算,我想先给大家看个例子:

#includeint main(){int a = 5;int b = 2;float c = a / b;printf("%d / %d = %f\n", a, b, c);return 0;}

  代码的运行结果是什么?会得到 2.52.52.5 吗?让我们一起来看看运行结果:


 为什么会这样呢?明明存放结果的变量类型已经是浮点型 ( f l o a t )(float)(float) 了呀,为什么小数部分还是 000 呢?
 原因在于C语言中的整数除法是整除,只会返回整数部分,丢弃小数部分.

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

参考代码:

#includeint 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)

参考代码:

#includeint main(){int a = 5;float b = 2;float c = a / b;printf("%d / %f = %f\n", a, b, c);return 0;}

运行结果:

2.3、 % 操作符

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

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

参考代码:

#includeint 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、 原码、 反码、 补码

  位移操作符实质上是对数据的二进制位进行操作,因此了解整数二进制表示方法至关重要。

  整数的二进制表示方法三种:

  • 原码
  • 反码
  • 补码

  对于(signed)int(signed )int signedint (有符号整型),它的三种表示方法均分为符号位数值位两个部分。二进制序列中,最高位被当做符号位,剩余的都是数值位

 符号位都是用 00 0 表示,用 11 1 表示

  • 正整数的原、 反、 补码都相同
  • 负整数的三种表示方法各不相同

(1)原码

  直接将数值按照正负数的形式翻译成二进制得到的就是原码。

示例一:

#includeint main(){int a = -10;return 0;}
  • -10是存放在 aaa 中, aaa 是整型变量,有4个字节,即32个bit位
  • 10的二进制为:1010
  • -10为负数,负数用最高位 1 来表示
  • 因为一共4个字节,中间位用 0 补为位

(2)反码

  将原码的符号位不变,其他位依次按位去反,即反码

(3)补码

  反码 +1 就得到补码

  
示例二:

#includeint main(){int a = 10;return 0;}

而对于正数来说,它的三种表示方法相同:

  

补码得到原码:

  • −1-1 1 ,取反
  • 取反,+1+1 +1

对于整型来说:数据存放内存中都是用补码进行运算和存储

原因在于,使用补码,可以将符号位和数值位统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码相互转换,其运算过程是相同的(取反,+1+1 +1),不需要额外的硬件电路。

3.2、 左移操作符 <<

  
 移位规则:左边抛弃、右边补 0


注:调试时,在需要观测的整型变量后面加上 “ , b,b,b ”,即为其二进制表示。

运行结果:

图示:

  

3.3、 右移操作符 >>

  移位规则:首先右移运算分为两种:

  • 逻辑右移:左边用 000 填充,右边丢弃
  • 算术右移:左边用原该值的符号位填充,右边丢弃

注:大部分编译器都是算术右移

运行结果:

图示:


注:对于移位操作符,不要移动负数位,这个标准是未定义的!

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)前置++
#includeint main(){int a = 10;int b = ++a;printf("%d %d\n", a, b);return 0;}

运行结果:

  这段代码中,aa a 原来是 10 ,先 +1 , aaa 变为11使用就是赋值给 bbb bb b 得到的也是11,所以 aa abb b 都是 11。

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

相当于以下代码:

#includeint main(){int a = 10;a = a + 1;int b = a;printf("%d %d\n", a, b);return 0;}

  

(2)后置++
#includeint main(){int a = 10;int b = a++;printf("%d %d\n", a, b);return 0;}

运行结果:

  上述代码中, aa a 原来是10,先使用,就是先赋值给 bbbbb b 得到了10,然后 aaa 自增成了11,所以运行结果 aa a 是11,bb b 是10.

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

相当于如下代码:

#includeint main(){int a = 10;int b = a;a = a + 1; printf("%d %d\n", a, b);return 0;}

  

(3) – –

  – – 操作符和 ++ 原理一样,只是自增变成自减而已,这里就不再过多赘述了,下面给段代码大家一起来检验一下吧

#includeint main(){int a = 10;int b = a--;int c = --a;printf("%d %d %d\n", a, b, c);return 0;}

运行结果:

 怎么样,你做对了吗?

6.2、 + 和 –

  这里的 + 是正号, 是负号,他们都是单目操作符

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

#includeint main(){int a = 10;int b = +10;printf("%d %d\n", a, b);return 0;}



 操作符 用来改变一个正值的正负号,在负数的前面加上 就会得到正数,在正数前面加上就会得到负数。

#includeint 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、 强制类型转换

  在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,如下:

(类型)

  
请看代码:

#includeint main(){int a = 3.14;printf("%d/n", a);return 0;}


 为了消除这个警告,我们可以使用强制类型转换:

#includeint main(){int a = (int)3.14;printf("%d\n", a);return 0;}

  但,俗话说:强扭的瓜不甜,我们使用强制类型转换都是万不得已的时候使用,如果不需要强制类型转换就能实现代码,这样自然最好。

6.4、sizeof

  看到这,是不是很惊讶呢,没错!sizeofsizeof sizeof 并不是函数,而是一个操作符
sizeofsizeof sizeof 是一个关键字,也是一个操作符是专门用来计算类型长度的,单位是字节

s i z e o fsizeofsizeof单目操作符,操作数可以是类型,也可以是变量或者表达式

sizeof(类型)sizeof 表达式
  • s i z e o fsizeofsizeof 的操作数如果不是类型,是表达式的时候,可以省略后边的括号的。
  • s i z e o fsizeofsizeof 后边的表达式是不真实参与运算的,根据表达式的类型雷得出大小。
  • s i z e o fsizeofsizeof 的计算结果是 s i z esizesize_ ttt 类型的。

  sizeofsizeof sizeof 操作符的返回值,C语言只规定是无符号整数,并没有规定具体类型,而是留给系统自己决定。sizeofsizeof sizeof 到底返回什么类型,在不同的系统中,返回值的类型有可能是 unsignedunsigned unsignedintint int ,也有可能是 unsignedunsigned unsignedlonglong long,甚至是 unsignedunsigned unsignedlonglong longlonglong long。他们对应的 printf()printf() printf() 占位符分别是 %uu u、%lulu lu和 %llullu llu。这样不利于程序的可移植性。
 C语言提供了一个解决方法,创造了一个类型别名 sizesize size_tt t,用来统一表示 sizeofsizeof sizeof 的返回值类型。对应当前系统的 sizeofsizeof sizeof 的返回值类型,可能是unsignedunsigned unsignedintint int ,也可能是unsignedunsigned unsignedlonglong longlonglong long

比如:

#includeint 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;}


sizeofsizeof sizeof中表达式不计算 !

#includeint 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 fsizeofsizeof 在进行编译的时候,就根据表达式的类型确定了,而表达式的执行却要在程序运行期间才能执行,在编译期间已经将 sizeofsizeof sizeof 处理掉了,所以在运行期间就不会再执行表达式了。

6.5、!

  在C语言中:0 为假,非 0 为真(负数也为真)
操作符作用是:逻辑取反

图示:

  
 比如,我们有一个变量 flagflag flag,如果 flagflag flag 为假,要做一个什么事情,就可以这样写代码:

#includeint main(){int flag = 0;if (!flag){printf("do something\n");}return 0;}

  如果 flagflag flag 为真,!flag!flag flag 就是假,如果 flagflag flag 为假, !flag!flag flag 就是真
 所以上面的代码意思是 flagflag flag 为假,执行 i fifif 语句中的代码.


剩下的两个单目操作符 & ∗*,将在之后指针内容一起介绍,敬请期待!