美文网首页
[C语言]你真的了解C语言吗之位运算操作

[C语言]你真的了解C语言吗之位运算操作

作者: IAMLIUBO | 来源:发表于2019-03-22 11:38 被阅读0次

    自C语言盛行以来,豪杰并起,执掌一方者不可胜数,Java比于C++,则年轻而效低,然Java遂能克C++,
    以弱为强者,非惟效率,抑亦人和也。今Java已拥亿万之众,移动端后端并行,此诚不可与争锋。
    Python揭竿而起,随者之众不可胜数,各方类库亦犹过江之鳞,应天时,人和也。
    JavaScript据浏览器,已历n世,国险而源富,此二者可以为援而不可图也。自node出世,Js风生水起,
    前后端并行,犹有冲击Java之势。然库多而欠理,npm,webpack一举成名。后React,Vue,Angular等应运而生,
    大前端之势浩大,恐怖如斯。然鱼龙混杂,犹互旋之水,难复清明。

    至于移动,Android、IOS双足对立。Android本忠Java,然跨平台之势如历史洪流,不可阻也,
    外有Hybrid,Weex,ReactNative,Flutter纷涌不绝,内有Kotlin暗刀一击,
    Android-Java帝国犹有崩摧之势,然习百技,纳百艺,Coder之能也,此乱世之道,更需多技傍身。

    C语言面相过程,年虽老矣,尚有余力,底层之功,莫能与之争,实不可因其老而蔑之。
    面相过程之于编程,创现世之基业,劳苦之功,无人能出奇右,实无人可蔑之。
    函数式之于编程,新生之思,思之至纯,虽年幼却难掩其芒。实不可因其异而蔑之。
    此三者,切不可盲从而身陷,亦不可斥而尊宗,习其思,用于正道,方为上上。

    来自《隆中对》----张风捷特烈

    依稀还记得大一学C语言的时光,讲真的,那是我对编程的初次认识(本人非计科专业),哪成想几年过去了,现在几乎终日与它为伴~要知道在嵌入式开发当中几乎是离不开C语言的,因为C语言是最接近硬件的一门语言,相比于汇编是更加适合人类开发和阅读的一门语言,虽然现在嵌入式开发也有了别的语言,比如Lua,MicroPython,JS等等,但是相对于C语言的执行效率是远远落后的,所以做嵌入式开发学好C语言是很有必要的。虽然自己每天都有写C代码,可是对于C语言的理解还是远远不够的,就比如有些概念还停留在知道、晓得、懂一点的水平,这是制约自己发展的一大重要因素啊!

    是时候重新拾起来,仔细去学习一番了,之前单学C语言的时候,总觉得少一点东西,为什么这么说呢?可能只是为了学而学,并不知道在实际应用中该怎么用,有或者在哪些实际情况中会用到,所以就是缺点东西!现在不一样了,有了一个实际的载体(硬件),知道这一块功能如果去实现该怎么写,如果优化该怎么优化,所以现在对C语言有了很多新的理解。

    这两天就对这个C语言中的位运算重新学习了一边,可见当时确实学的浅薄,简单一写,加深下理解~

    不知道你在开发单片机的过程中有没有遇到过下面的运算符,又或者你能准确的说出每一个运算符的用法吗?

    • &
    • |
    • ^
    • ~
    • <<

    我们先来理解下什么是位吧,可能你还记得当时书本中说过的字节和位的区别:

    • 位 计算机中最小的存储单位
    • 字节 计算机中最小的数据单位

    那我们该怎么理解呢?我们首先要知道计算机中的数据都是0和1的顺序组合,所有的数据最后都是0和1的特定排列,每8个0和1的排列就组成了最小的数据单位->字节,也就是每个字节都是由8位0和1组成的,就比如说数字“0”,在存储器当中是以“00110000”这样的8位连续的0和1排列的,这是我们通过数据在内存中怎么去保存的去理解位和字节的区别。

    那在实际开发当中,位运算有什么用呢?我们实际开发中会用到位操作吗?答案当然是肯定的,最常见的例子就是在某些与寄存器打交道的地方,我们需要频繁的进行微操作,因为寄存中的每一位置“1”或者置“0”都是有不同含义的,就拿比较常见的STM32单片机来说吧,我们看一下它寄存器说明:

    image

    这是APB1外设复位寄存器的说明。可以看见一共有0~31->32位,STM32的寄存器都是32位的,这个表对此寄存器的每一位是复位哪一个外设的,都做了详细注释,就比如说我们复位DAC(数模转换)吧,我们只需让此寄存器的第29位置“1”就可以让DAC外设复位了。

    不闲扯了,来复习我们的C语言吧还是,刚刚给出的6种C语言中的位运算符都知道什么意思了吗?如果还没有,我们来复习一下,下面给出的实际位操作实列都是二进制位模式:

    • & 按位与

    00000110 & 00001011 = 00000010

    • | 按位或

    00000110 | 00001011 = 00001111

    • ^ 按位异或

    00000110 ^ 00001011 = 00001101

    • ~ 按位取反

    ~ 00000110 = 11111001

    • 向右移位

    00000001(结果) = 00000110 >> 2(移动2位)

    • << 向左移位

    00011000(结果) = 00000110 << 2(移动2位)

    在移位运算中,有两点需要注意:

    1. 向左移位的时候,右边多出来的位用0补齐,超出左边边界抛弃。
    2. 向右移位的时候,左边多出来的位需要根据有无符号或者是否是负数去处理,无符号或者有符号但非负数,左边多出来的位用0补齐,如果是负数,具体填充位的内容是根据编译器决定的。

    思考:

    1. int a = 7,将a的第7位置“1”,不改变其它位。

    ① a |= (0x01 << 7)

    ② a |= 0x80

    2. int a = 7,将a的第1位置“0”,不改变其它位。

    ① a &= ~ (0x01 << 1)

    ② a &= ~ 0x02

    ③ a ^= 0x20

    3. int a = 7,将a的第7位置“1”后再清除,不改变其它位。

    置“1”:

    ① x = a | (0x01 << 7)

    ② x = a | 0x80

    清除:

    ① a = x & (~(0x01 << 7))

    ② a = x & (~0x80)

    ③ a = x ^ 0x80

    4. int a = 7,将a的第1位置“0”后再置“1”,不改变其它位。

    置“0”:

    ① x = a & (~ (0x01 << 1))

    ② x = a & (~ 0x20)

    ③ x = a ^ 0x20

    置“1”:

    ① x = a | (0x01 << 1)

    ② x = a | 0x20

    简单写两个实列,帮助记忆,在嵌入式开发当中置位和清除是比较常见的操作,只有熟练操作才可以运用的更加熟练,一般为了对位操作的方便性和方便移植,都会写成下面的宏定义:

    #define setbit(x,y) x |= (0x01 << y)
    #define clrbit(x,y) x &= ~(0x01 << y)
    #define reversebit(x,y) x ^= (0x01 << y)
    #define getbit(x,y)  ((x) >> (y) & 0x01)
    
    

    先写到这里,如果有表述错误,还请指出我好修正。

    相关文章

      网友评论

          本文标题:[C语言]你真的了解C语言吗之位运算操作

          本文链接:https://www.haomeiwen.com/subject/guslvqtx.html