一起学习C语言:C语言基本语法(四)

  上一篇 <一起学习C语言:C语言基本语法(三)> 中,我们了解了关系运算符的语法和不同的应用场景中的选择。本篇文章中,我们分析位运算符的用法,并通过几个示例分析不同的位运算符的运算方式和负数位移的保护方式。


章节预览:

4. 位运算符
4.1 位运算符用法
4.1.1 按位与运算
4.1.2 按位或运算
4.1.3 按位取反运算
4.1.4 按位异或
4.1.5 按位左移
4.1.6 按位右移
4.2 回顾赋值运算符
目录预览


章节内容:


4. 位运算符


  现在我们已经具有不错的基础了,接下来的学习会变的比较简单。位运算符这部分知识也非常重要,在接来下的分支结构中,我们也会用到这部分知识。


4.1 位运算符用法

  位运算符分为六种:位与(&)、位或(|)、取反(~)、异或(^)、左移(<<)、右移(>>)。参考图4-14-2


位运算符 含义 举例(二进制数) 说明
& 按位与 c = a & b 如果a为1而且 b为1,则c等于1,否则c等于0
| 按位或 c = a | b 如果a为1或 b为1,或者a和b都为1,则c等于1,否则c等于0
~ 按位取反 c = ~a 如果a为1,则c等于0,否则c等于1
^ 按位异或 c = a ^ b 如果a 与 b的值不同,则c等于1,否则c等于0
4-1 位运算符表A

位运算符 含义 举例(十进制数) 说明
<< 左移 c = a << b a 向左移动 b位,如果a和b都为1时,则1向左移动1位,即3,二进制数10
>> 右移 c = a >> b a 向右移动 b位,如果a为7,b为2时,则7向右移动2位,即1,二进制数1
4-2 位运算符表B

关于位运算符:

  位运算符只对整数起作用,而浮点数则不能使用。如果在浮点数中使用位运算符,编译器将会报错,原因是:浮点数不能直接使用二进制运算,它必须保证符号域和指数域不被改变。


4.1.1 按位与运算

  “位与”运算符和“与”运算符用法相同,但“位与”运算符是对于两个相同位(bit)的运算,而“与”运算符是两个条件之间的运算。下面我们通过几个例子来了解“位与”运算符的实际用法。


  【例4.10】 通过“位与”运算符,计算出char类型数字“5 & 10”的位运算结果。

      运算过程

        首先把5和10转换到二进制数字 (2)

          数字5:
            5 / 2 = 2 余 1
            2 / 2 = 1 余 0
            1 / 2 = 0 余 1
            取余得到101,调换后得到101,补齐八位 (3) 得到 0000 0101。

          数字10:
            10 / 2 = 5 余 0
            5 / 2 = 2 余 1
            2 / 2 = 1 余 0
            1 / 2 = 0 余 1
            取余得到0101,调换后得到1010,补齐八位得到 0000 1010。


(2):十进制正数转换到二进制正数的方法:数值除2取余数,一直到数值除为0为止,最后调换结果的前后位置。

(3):当二进制正数不足指定类型位数时,将采用高位补0方式,对齐数据。


        得到完整示例:0000 0101 & 0000 1010,现在“位与”运算,参考图4-3


bit(位) 7bit(符号位) 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 5 0 0 0 0 0 1 0 1
二进制数字 10 0 0 0 0 1 0 1 0
“位与”结果 0 0 0 0 0 0 0 0
4-3 数字5 & 数字10

        运算结果

            5 & 10的结果为0000 0000,即十进制数字0。


  【例4.11】 通过“位与”运算符,计算出short 类型数字“-128 & 169”的位运算结果。

      运算过程

        首先把-128和169转换到二进制数字 (4)

          数字-128:
            128 / 2 = 64 余 0
            64 / 2 = 32 余 0
            32 / 2 = 16 余 0
            16 / 2 = 8 余 0
            8 / 2 = 4 余 0
            4 / 2 = 2 余 0
            2 / 2 = 1 余 0
            1 / 2 = 0 余 1
            取余得到0000 0001,调换后得到1000 0000,然后所有位取反得到0111 1111,加1后得到结果1000 0000 (5),最后补齐十六位 (6) 为 1111 1111 1000 0000。


(4):十进制负数转换到二进制负数的方法:正数值除2取余数,一直到数值除为0为止,然后调换结果的前后位置,并把所有位取反后(即0为1,1为0),加1。

(5):二进制相加方式为:满二进一位(bit),当前位取零。即两个二进制数的N位相加值为二时,N+1位值加一,N位值变为零。

(6):当二进制负数不足指定类型位数时,将采用高位补1方式,对齐数据。


         数字169:
           169 / 2 = 84 余 1
           84 / 2 = 42 余 0
           42 / 2 = 21 余 0
           21 / 2 = 10 余 1
           10 / 2 = 5 余 0
           5 / 2 = 2 余 1
           2 / 2 = 1 余 0
           1 / 2 = 0 余 1
           取余得到 1001 0101,调换后得到1010 1001,最后补齐十六位为 0000 0000 1010 1001。


        得到完整示例:1111 1111 1000 0000 & 0000 0000 1010 1001,现在“位与”运算,参考 图4-4


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -128 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
二进制数字 169 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1
“位与”结果 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
4-4 数字-128 & 数字169

        运算结果

            -128 & 169的结果为0000 0000 1000 0000,即十进制数字128。


  【例4.12】 通过“位与”运算符,计算出short数字“-128 & -169”的位运算结果。

      运算过程

        把-128和-169转换到二进制数字:

          数字-128:
            128 / 2 = 64 余 0
            64 / 2 = 32 余 0
            32 / 2 = 16 余 0
            16 / 2 = 8 余 0
            8 / 2 = 4 余 0
            4 / 2 = 2 余 0
            2 / 2 = 1 余 0
            1 / 2 = 0 余 1
            取余得到 0000 0001,调换后得到1000 0000,然后所有位取反得到0111 1111,加1后得到结果1000 0000,最后补齐十六位为 1111 1111 1000 0000。


          数字-169:
            169 / 2 = 84 余 1
            84 / 2 = 42 余 0
            42 / 2 = 21 余 0
            21 / 2 = 10 余 1
            10 / 2 = 5 余 0
            5 / 2 = 2 余 1
            2 / 2 = 1 余 0
            1 / 2 = 0 余 1
            取余得到 1001 0101,调换后得到1010 1001,然后所有位取反得到0101 0110,加1后得到结果0101 0111,最后补齐十六位为 1111 1111 0101 0111。


        得到完整示例:1111 1111 1000 0000 & 1111 1111 0101 0111,现在“位与”运算,参考图4-5


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -128 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
二进制数字 -169 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1
“位与”结果 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
4-5 数字-128 & 数字-169

        运算结果

            -128 & -169的结果为1111 1111 0000 0000,转换到十进制负数数字:得到 1111 1111 0000 0000,取反后得到 0000 0000 1111 1111,加1后得到1 0000 0000,取出符号值1(运算过程中,符号值的问题可以参考<一起学习C语言:C语言数据类型(一)>),即-256。


4.1.2 按位或运算

  “位或”运算符和“或”运算符用法相同,但“位或”运算符是对于两个相同位(bit)的运算,而“或”运算符是两个条件之间的运算。下面我们继续通过几个例子来了解“位或”运算符的实际用法。

  【例4.13】 通过“位或”运算符,计算出char类型数字“9 | 12”的位运算结果。

      运算过程

          首先把5和10转换到二进制数字,转换方式与上述内容相同,后面省略这些步骤:

          数字9转换二进制数字得到 1001,补齐八位得到 0000 1001。数字12转换二进制数字得到 1100,补齐八位得到 0000 1100。


        得到完整示例:0000 1001 | 0000 1100,现在“位或”运算,参考图4-6


bit(位) 7bit(符号位) 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 9 0 0 0 0 1 0 0 1
二进制数字 12 0 0 0 0 1 1 0 0
“位或”结果 0 0 0 0 1 1 0 1
4-6 数字9 | 数字12

        运算结果

            9 | 12的结果为0000 1101,即十进制数字13。


  【例4.14】 通过“位或”运算符,计算出short 类型数字“-226 | 189”的位运算结果。

      运算过程

          数字-226转换二进制数字得到 0001 1110,补齐十六位得到 1111 1111 0001 1110。数字189进制数字得到 1011 1101,补齐十六位得到 0000 0000 1011 1101。


        得到完整示例:1111 1111 0001 1110 | 0000 0000 1011 1101,现在“位或”运算,参考图4-7


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -226 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 0
二进制数字 189 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 1
“位或”结果 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
4-7 数字-226 | 数字189

        运算结果

            -226 | 189的结果为1111 1111 1011 1111,即十进制数字-65。


  【例4.15】 通过“位或”运算符,计算出short 类型数字“-228 | -259”的位运算结果。

      运算过程

          数字-228转换二进制数字得到 0001 1100,补齐十六位得到 1111 1111 0001 1100。数字-259进制数字得到0 1111 1101,补齐十六位得到 1111 1110 1111 1101。


        得到完整示例:1111 1111 0001 1100 | 1111 1110 1111 1101,现在“位或”运算,参考图4-8


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -228 1 1 1 1 1 1 1 1 0 0 0 1 1 1 0 0
二进制数字 -259 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1
“位或”结果 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1
4-8 数字-228 | 数字-259

        运算结果

            -228 | -259的结果为1111 1111 1111 1101,即十进制数字-3。


4.1.3 按位取反运算

  “取反”运算符和“非”运算符用法相同,但“取反”运算符是对于两个相同位(bit)的运算,而“非”运算符是两个条件之间的运算。下面我们继续通过几个例子来了解“取反”运算符的实际用法。


  【例4.16】 通过“取反”运算符,计算出char类型数字“~89”的位运算结果。

      运算过程

          数字89转换二进制数字得到 0101 1001,得到完整示例:~0101 1001,现在“取反”运算,参考图4-9


bit(位) 7bit(符号位) 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 89 0 1 0 1 1 0 0 1
“取反”结果 1 0 1 0 0 1 1 0
4-9 数字~89

        运算结果

            ~89的结果为1010 0110,即十进制数字-90。


  【例4.17】 通过“取反”运算符,计算出short类型数字“~-6536”的位运算结果。

      运算过程

          数字-6536转换二进制数字得到 1110 0110 0111 1000,得到完整示例:~1110 0110 0111 1000,现在“取反”运算,参考图4-10


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -6536 1 1 1 0 0 1 1 0 0 1 1 1 1 0 0 0
“取反”结果 0 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1
4-10 数字~-6536

        运算结果

            ~-6536的结果为0001 1001 1000 0111,即十进制数字6535。


4.1.4 按位异或

  “异或”运算符的用法有些独特,当两个二进制数字“异或”运算时,相同为零,不同为一。如“1 ^ 1”和“0 ^ 0”它们的值为0,而“1 ^ 0”和“0 ^ 1”它们的值则为1。下面我们继续通过几个例子来了解“异或”运算符的实际用法。

  【例4.18】 通过“异或”运算符,计算出char类型数字“89 ^ 89”的位运算结果。

      运算过程

          数字89转换二进制数字得到 0101 1001,得到完整示例:0101 1001 ^ 0101 1001,现在“异或”运算,参考图4-11


bit(位) 7bit(符号位) 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 89 0 1 0 1 1 0 0 1
二进制数字 89 0 1 0 1 1 0 0 1
“异或”结果 0 0 0 0 0 0 0 0
4-11 数字89 ^ 89

        运算结果

            89 ^ 89的结果为0000 0000,即十进制数字0。


  【例4.19】 通过“异或”运算符,计算出short类型数字“-156 ^ 215”的位运算结果。

      运算过程

          数字-156转换二进制数字得到 1111 1111 0110 0100,数字215转换二进制数字得到 0000 0000 1101 0111,得到完整示例:1111 1111 0110 0100 ^ 0000 0000 1101 0111。现在“异或”运算,参考图4-12


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -156 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 0
二进制数字 215 0 0 0 0 0 0 0 0 1 1 0 1 0 1 1 1
“异或”结果 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 1
4-12 数字-156 ^ 215

        运算结果

            -156 ^ 215的结果为1111 1111 1011 0011,即十进制数字-77。


  【例4.20】 通过“异或”运算符,计算出short类型数字“-156 ^ -512”的位运算结果。

      运算过程

          数字-156转换二进制数字得到 1111 1111 0110 0100,数字-512转换二进制数字得到1111 1110 0000 0000,得到完整示例:1111 1111 0110 0100 ^ 1111 1110 0000 0000。现在“异或”运算,参考图4-13


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -156 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 0
二进制数字 -512 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
“异或”结果 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 0
图4-13 数字-156 ^ -512

        运算结果

            -156 ^ -512的结果为0000 0001 0110 0100,即十进制数字356。


4.1.5 按位左移

  “左移”运算符一般用做在“有效位数”范围内,低位到高位的平移活动。比如char型数据 “1000 0001 << 1”(7),它的所有位都向高位移动一位,得到0000 0010,其中最高位被丢弃,最低位补0对齐数据。示例中,这种数据不在有效位移范围内,因为这样会丢失有效数据,即数据溢出 (8)。下面我们继续通过几个例子来了解“左移”运算符的实际用法。


(7):“左移”运算符后面的数字表示“整体”向左平移的位数,数字如果为0,则不移动。另外,运算符后面的数字不能是负数。

(8):如果我们只是把某一位或某几位的值放到指定位置,可以这么使用。比如1000 0001低四位平移到高四位可以这些写:
     char a = 129 << 4 & 0xf0; // 0xf0,即二进制数字1111 0000
     首先把低四位移动到高四位,然后通过“位与”运算符做到保护高四位,清除低四位的任务,即避免出现无效数据。


  【例4.21】 通过“左移”运算符,计算出short类型数字“1255 << 2”的位运算结果。

      运算过程

          数字1255转换二进制数字得到 0000 0100 1110 0111,得到完整示例:0000 0100 1110 0111 << 2,现在“左移”运算,参考图4-14


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 1255 0 0 0 0 0 1 0 0 1 1 1 0 0 1 1 1
“左移”结果 0 0 0 1 0 0 1 1 1 0 0 1 1 1 0 0
4-14 数字1255 << 2

        运算结果

            1255 << 2的结果为0001 0011 1001 1100,即十进制数字5020。当然,为了保证数据的准确性,我们应该保护有效数据,清除移位可能造成的无效数据。我们左移了两位,应该写成 1255 << 2 & 0xfffc (9)


(9):当数据过大时,计算起来会比较麻烦,我们可以通过“程序员”计算器,做这部分功能。 【例4.21】 中,short类型数字左移了两位,保护数据可以写成 1111 1111 1111 1100,通过计算器可以转换为十六进制数0xfffc。


  【例4.22】 通过“左移”运算符,计算出short类型数字“-2345 << 2”的位运算结果。

      运算过程

          数字-2345转换二进制数字得到 1111 0110 1101 0111,得到完整示例:1111 0110 1101 0111 << 2,现在“左移”运算,参考图4-15


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -2345 1 1 1 1 0 1 1 0 1 1 0 1 0 1 1 1
“左移”结果 1 1 0 1 1 0 1 1 0 1 0 1 1 1 0 0
图4-15 数字-2345 << 2

        运算结果

            -2345 << 2的结果为1101 1011 0101 1100,即十进制数字-9380。数据保护形式:-2345 << 2 & 0xfffc。


4.1.6 按位右移

  “右移”运算符一般用做在“有效位数”范围内,高位到低位的平移活动。“右移”运算符 (10) 可以看做“左移”运算符的反向移位。下面我们继续通过几个例子来了解“右移”运算符的实际用法。


(10):“右移”运算符后面的数字表示 “整体”向右平移的位数,数字如果为0,则不移动。另外,运算符后面的数字也不能是负数。


  【例4.23】 通过“右移”运算符,计算出short类型数字“1234 >> 4”的位运算结果。

      运算过程

          数字1234转换二进制数字得到 0000 0100 1101 0010,得到完整示例:0000 0100 1101 0010 >> 4,现在“右移”运算,参考图4-16


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 1234 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 0
“右移”结果 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1
4-16 数字1234 >> 4

        运算结果

            1234 >> 4的结果为0000 0000 0100 1101,即十进制数字77。当然,为了保证数据的准确性,“右移”运算也应该保护有效数据,清除移位可能造成的无效数据。我们右移了四位,应该写成 1234 >> 4 & 0x0fff (11)


(11):“右移”运算的保护方式为保护低位数据,清除高位数据。【例4.23】 中,short类型数字右移了四位,保护数据可以写成 0000 1111 1111 1111,通过计算器可以转换为十六进制数0x0fff。


  【例4.24】 通过“右移”运算符,计算出short类型数字“-12567 >> 4”的位运算结果。

      运算过程

          数字-12567转换二进制数字得到 1100 1110 1110 1001,得到完整示例:1100 1110 1110 1001 >> 4,现在“右移”运算,参考图4-17


bit(位) 15bit(符号位) 14bit 13bit 12bit 11bit 10bit 9bit 8bit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0bit
二进制数字 -12567 1 1 0 0 1 1 1 0 1 1 1 0 1 0 0 1
“右移”结果 1 1 1 1 1 1 0 0 1 1 1 0 1 1 1 0
4-17 数字-12567 >> 4

        运算结果

            -12567 >> 4的结果为1111 1100 1110 1110 (11),即十进制数字-786。当数值是负数时,我们需要根据实际情况来编写保护形式:

                1. 如果,我们还是按照高四位清零,低十二位保护的形式运算,得到的结果为3310,但与实际结果不同。
                2. 如果我们只是为了获取低十二位数值,可以采用上述形式进行保护数据。


(11):负数“右移”运算时,采用高位补1形式对齐数据。


4.2 回顾赋值运算符

  现在,我们学会了位运算符的用法,掌握这部分知识可以让我们在编程中,更加得心应手。接下来,我们回顾一下赋值运算符表,了解位运算在赋值运算中的另一种写法,参考图4-18


赋值运算符号 含义 示例 结果 原理
= 简单的赋值运算符 a = 1 + 2 a = 3 a = 1 + 2等同于算术表达式1 + 2 = a
+= 加且赋值运算符 a += 2(a = 0的情况下) a = 2 a += 2等同于a = a + 2
-= 减且赋值运算符 a -= 2(a = 2的情况下) a = 0 a -= 2等同于a = a - 2
*= 乘且赋值运算符 a *= 2(a = 2的情况下) a = 4 a *= 2等同于a = a * 2
/= 除且赋值运算符 a /= 2(a = 2的情况下) a = 1 a /= 2等同于a = a / 2
%= 求模且赋值运算符 a %= 2(a = 2的情况下) a = 0 a %= 2等同于a = a % 2
<<= 左移且赋值运算符 a <<= 2(a = 2的情况下) a = 8 a <<= 2等同于a = a << 2
>>= 右移且赋值运算符 a >>= 1(a = 4的情况下) a = 2 a >>= 1等同于a = a >> 1
&= 按位与且赋值运算符 a &= 2(a = 2的情况下) a = 2 a &= 2等同于a = a & 2
^= 按位异或且赋值运算符 a ^= 2(a = 2的情况下) a = 0 a ^= 2等同于a = a ^ 2
|= 按位或且赋值运算符 a |= 2(a = 2的情况下) a = 2 a |= 2等同于a = a | 2
4-18 赋值运算符表

  关于位运算这部分,需要分析透彻所需功能,再选择对应的位运算符号。移位运算时,使用数据保护形式可以避免出现意料之外的数值结果。另外,一行语句里如果同时使用多个位运算符,应使用小括号进行代码保护,这样可以保证它们之间的执行顺序正确。参考示例:
      (a >> 3) << 2,这种形式可以很轻松看出代码先执行a >> 3这部分。


目录预览


<一起学习C语言:C语言发展历程以及定制学习计划>
<一起学习C语言:初步进入编程世界(一)>
<一起学习C语言:初步进入编程世界(二)>
<一起学习C语言:初步进入编程世界(三)>
<一起学习C语言:C语言数据类型(一)>
<一起学习C语言:C语言数据类型(二)>
<一起学习C语言:C语言数据类型(三)>
<一起学习C语言:C语言基本语法(一)>
<一起学习C语言:C语言基本语法(二)>
<一起学习C语言:C语言基本语法(三)>

发布了98 篇原创文章 · 获赞 219 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/a29562268/article/details/104446259