<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                【34.1 “左移”運算。】 “左移”運算也是以位為單位進行運算的。位是指二進制中的某一位,位只能是0或者1。欲理解某個數“左移”運算的內部規律,必先把該數展開成二進制的格式,然后才好分析。“左移”運算的符號是“<<”,它的通用格式如下: “保存變量”=“被移數”<<n; 運算規律是:“被移數”先被復制一份放到某個隱蔽的臨時變量(也稱作寄存器),然后對此臨時變量展開成二進制的格式,左邊是高位,右邊是低位,此二進制格式的臨時變量被整體由右往左移動了n位,原來左邊的高n位數據被直接覆蓋,而右邊由于數據位移動而新空出的低n位數據被直接填入0,最后再把移位運算的結果存入“保存變量”。多問一句,這行代碼執行完畢后,“保存變量”和“被移數”到底哪個變量發生了變化,哪個變量維持不變?大家記住,只有賦值語句“=”左邊的“保存變量”發生數值變化,而右邊的“被移數”沒有發生變化,因為“被移數”被操作的不是它自己本身,而是它的復制品替身(某個隱蔽的臨時變量,也稱寄存器)。這條規律對“加、減、乘、除、與、或、異或、非、取反”等運算都是適用的,重要的事情再重復一次,這條規律就是:只有賦值語句“=”左邊的“保存變量”發生數值變化,而賦值語句“=”右邊的“運算變量”本身不會發生變化,因為“運算變量”被操作的不是它自己本身,而是它的復制品替身(某個隱蔽的臨時變量,也稱寄存器)。 上述通用格式中的n代表被一次左移的位數,可以取0,當n等于0的時候,代表左移0位,其實就是數值維持原來的樣子沒有發生變化。 現在舉一個完整的例子來分析“<<”左移運算的規律。有兩個unsigned char類型的變量a和b,它們的數值都是十進制的5,求a=a<<1和b=b<<2的結果分別是多少?分析步驟如下: 第一步:先把a和b變量原來的數值以二進制的格式展開。十進制轉二進制的方法請參考前面第14,15,16節的內容。 a變量是十進制5,它的二進制格式是: 00000101。 b變量是十進制5,它的二進制格式是: 00000101。 第二步:將a左移1位,將b左移2位。 (1)a=a<<1,就是將a左移1位。 a左移前是 -> 00000101 a左移1位后是 -> 00001010 結果分析:把二進制的00001010轉換成十六進制是:0x0A。轉換成十進制是10。所以a初始值是5,左移1位后的結果是10。 (2)b=b<<2,就是將b左移2位。 b左移前是 -> 00000101 b左移2位后是 -> 00010100 結果分析:把二進制的00010100轉換成十六進制是:0x14。轉換成十進制是20。所以b初始值是5,左移2位后的結果是20。 **【34.2** **“左移”與乘法的關系。】** 上面的例子,仔細觀察,發現一個規律:5左移1位就變成了10(相當于5乘以2),5左移2位就變成了20(相當于5乘以2再乘以2)。這個現象背后的規律是:在左移運算中,只要最高位不發生溢出的現象,那么每左移1位就相當于乘以2,左移2位相當于乘以2再乘以2,左移3位相當于乘以2再乘以2再乘以2......以此類推。這個規律反過來從乘法的角度看,也是成立的:某個數乘以2,就相當于左移1位,某個數乘以2再乘以2相當于左移2位,某個數乘以2再乘以2再乘以2相當于左移3位......以此類推。那么問題來了,同樣是達到乘以2的運算結果,從運算速度的角度對比,“左移”和“乘法”哪家強?答案是:一條左移語句的運算速度比一條乘法語句的運算速度要快很多倍。 **【34.3** **“左移”的常見應用之一:不同數據類型之間的合并。】** 比如有兩個unsigned char單字節的類型數據H和L,H的初始值是十六進制的0x12,L的初始值是十六進制的0x34,要將兩個單字節的H和L合并成一個unsigned int雙字節的數據c,其中H是高8位字節,L是低8位字節,合并成c后,c的值應該是十六進制的0x1234,此程序如何寫?就需要用到左移。程序分析如下: unsigned char H=0x12; //單字節 unsigned char L=0x34; //單字節 unsigned int c; //雙字節 c=H; //c的低8位被H覆蓋,也就是c的低8位得到了H的值。 c=c<<8; //及時把c的低8位移動到高8位,同時c原來的低8位被填入0 c=c+L; //此時c再加L,c的低8位就L的值。 程序運行結果:c就等于十六進制的0x1234,十進制是4660。 **【34.4** **“左移”的常見應用之二:聚焦在某個變量的某個位。】** 前面第31節講到“或”運算,其中講到可以對某個變量的某個位置1,當時是這樣講的,片段如下: “或”運算最常見的用途是可以指定一個變量的某位置1,其它位保持不變。比如一個unsigned char類型的變量b,數據長度一共是8位,從右往左: 想讓第0位置1,其它位保持不變,只需跟十六進制的0x01相“或”:b=b|0x01。 想讓第1位置1,其它位保持不變,只需跟十六進制的0x02相“或”:b=b|0x02。 想讓第2位置1,其它位保持不變,只需跟十六進制的0x04相“或”:b=b|0x04。 想讓第3位置1,其它位保持不變,只需跟十六進制的0x08相“或”:b=b|0x08。 想讓第4位置1,其它位保持不變,只需跟十六進制的0x10相“或”:b=b|0x10。 想讓第5位置1,其它位保持不變,只需跟十六進制的0x20相“或”:b=b|0x20。 想讓第6位置1,其它位保持不變,只需跟十六進制的0x40相“或”:b=b|0x40。 想讓第7位置1,其它位保持不變,只需跟十六進制的0x80相“或”:b=b|0x80。 但是這樣寫很多程序員會嫌它不直觀,哪里不直觀?就是0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80這些數不直觀,這些數只是代表了聚焦某個變量不同的位。如果把這些十六進制的數值換成左移的寫法,在閱讀上就非常清晰直觀了。比如:0x01可以用1<<0替代,0x02可以用1<<1替代,0x04可以用1<<2替代......0x80可以用1<<7替代。左移的n位,n就恰好代表了某個變量的某個位。于是,我們把上面的片段更改成左移的寫法后,如下: “或”運算最常見的用途是可以指定一個變量的某位置1,其它位保持不變。比如一個unsigned char類型的變量b,數據長度一共是8位,從右往左: 想讓第0位置1,其它位保持不變,只需:b=b|(1<<0)。 想讓第1位置1,其它位保持不變,只需:b=b|(1<<1)。 想讓第2位置1,其它位保持不變,只需:b=b|(1<<2)。 想讓第3位置1,其它位保持不變,只需:b=b|(1<<3)。 想讓第4位置1,其它位保持不變,只需:b=b|(1<<4)。 想讓第5位置1,其它位保持不變,只需:b=b|(1<<5)。 想讓第6位置1,其它位保持不變,只需:b=b|(1<<6)。 想讓第7位置1,其它位保持不變,只需:b=b|(1<<7)。 分析:這樣改進后,閱讀就很清晰直觀了,只是在程序代碼的效率速度方面,因為多增加了一條左移指令,意味著要多消耗一條指令的時間,那么到底該選擇哪種?其實各有利弊,應該根據個人的編程喜好和實際項目來取舍。很多32位的單片機在初始化寄存器的庫函數里大量應用這種左移的方法來操作,目的就是為了增加代碼可讀性。 根據上述規律,假設d原來等于十進制的84(十六進制是0x54,二進制是01010100),要想把此數據的第0位置1,只需d=d|(1<<0)。最終d的運算結果是十進制是85(十六進制是0x55,二進制是01010101)。 剛才上面講到第31節的“或”運算,其實在第30節的“與”運算中也是可以用這種左移的方法來聚焦,只是要多配合一條“取反”的指令才可以。“與”運算跟“或”運算剛剛相反,它是對某個變量的某個位清零,當時是這樣講的,片段如下: “與”運算最常見的用途是可以指定一個變量二進制格式的某位清零,其它位保持不變。比如一個unsigned char類型的變量b,數據長度一共是8位,從右往左: 想讓第0位清零,其它位保持不變,只需跟十六進制的0xfe相“與”:b=b&0xfe。 想讓第1位清零,其它位保持不變,只需跟十六進制的0xfd相“與”:b=b&0xfd。 想讓第2位清零,其它位保持不變,只需跟十六進制的0xfb相“與”:b=b&0xfb。 想讓第3位清零,其它位保持不變,只需跟十六進制的0xf7相“與”:b=b&0xf7。 想讓第4位清零,其它位保持不變,只需跟十六進制的0xef相“與”:b=b&0xef。 想讓第5位清零,其它位保持不變,只需跟十六進制的0xdf相“與”:b=b&0xdf。 想讓第6位清零,其它位保持不變,只需跟十六進制的0xbf相“與”:b=b&0xbf。 想讓第7位清零,其它位保持不變,只需跟十六進制的0x7f相“與”:b=b&0x7f。 但是這樣寫很多程序員會嫌它不直觀,哪里不直觀?就是0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f這些數不直觀,這些數只是代表了聚焦某個變量不同的位。如果把這些十六進制的數值換成左移的寫法,在閱讀上就非常清晰直觀了,但是注意,這里左移之后還要配一條“取反”語句。比如:0xfe可以用~(1<<0)替代,0xfd可以用~(1<<1)替代,0xfb可以用~(1<<2)替代......0x7f可以用~(1<<7)替代。左移的n位后再取反,n就恰好代表了某個變量的某個位。于是,我們把上面的片段更改成左移的寫法后,如下: “與”運算最常見的用途是可以指定一個變量二進制格式的某位清零,其它位保持不變。比如一個unsigned char類型的變量b,數據長度一共是8位,從右往左: 想讓第0位清零,其它位保持不變,只需:b=b&(~(1<<0))。 想讓第1位清零,其它位保持不變,只需:b=b&(~(1<<1))。 想讓第2位清零,其它位保持不變,只需:b=b&(~(1<<2))。 想讓第3位清零,其它位保持不變,只需:b=b&(~(1<<3))。 想讓第4位清零,其它位保持不變,只需:b=b&(~(1<<4))。 想讓第5位清零,其它位保持不變,只需:b=b&(~(1<<5))。 想讓第6位清零,其它位保持不變,只需:b=b&(~(1<<6))。 想讓第7位清零,其它位保持不變,只需:b=b&(~(1<<7))。 分析:這樣改進后,閱讀就很清晰直觀了,只是在程序代碼的效率速度方面,因為多增加了一條左移指令和一條取反指令,意味著要多消耗兩條指令的時間,那么到底該選擇哪種?其實各有利弊,應該根據個人的編程喜好和實際項目來取舍。很多32位的單片機在初始化寄存器的庫函數里大量應用這種左移的方法來操作,目的就是為了增加代碼可讀性。 根據上述規律,假設e原來等于十進制的85(十六進制是0x55,二進制是01010101),要想把此數據的第0位清零,只需e=e&(~(1<<0))。最終e的運算結果是十進制是84(十六進制是0x54,二進制是01010100)。 【34.5 左移運算的“左移簡寫”。】 當被移數是“保存變量”時,存在“左移簡寫”。 “保存變量”=“保存變量”<<n; 上述左移簡寫如下: “保存變量”<<=n; 比如: unsigned char f=1; unsigned char g=1; f<<=1; //就相當于f=f<<1; g<<=2; //就相當于g=g<<2; 【34.6 例程練習和分析。】 現在編寫一個程序來驗證剛才講到的“左移”運算: 程序代碼如下: /\*---C語言學習區域的開始。-----------------------------------------------\*/ void main() //主函數 { unsigned char a=5; unsigned char b=5; unsigned char H=0x12; //單字節 unsigned char L=0x34; //單字節 unsigned int c; //雙字節 unsigned char d=84; unsigned char e=85; unsigned char f=1; unsigned char g=1; //左移運算中蘊含著乘2的規律。 a=a<<1; //a左移1位,相當于a=a\*2,從原來的5變成了10。 b=b<<2; //b左移2位,相當于b=b\*2\*2,從原來的5變成了20。 //左移的應用之一:不同變量類型的合并。 c=H; //c的低8位被H覆蓋,也就是此時c的低8位得到了H的各位值。 c=c<<8; //及時把c的低8位移動到高8位,同時c原來的低8位被填入0 c=c+L; //此時c再加L,c的低8位就L的值。此時c得到了H和L合并而來的值。 //左移的應用之二:聚焦在某個變量的某個位。 d=d|(1<<0); //對第0位置1。 e=e&(~(1<<0)); //對第0位清零。 //左移簡寫。 f<<=1; //就相當于f=f<<1; g<<=2; //就相當于g=g<<2; View(a); //把第1個數a發送到電腦端的串口助手軟件上觀察。 View(b); //把第2個數b發送到電腦端的串口助手軟件上觀察。 View(c); //把第3個數c發送到電腦端的串口助手軟件上觀察。 View(d); //把第4個數d發送到電腦端的串口助手軟件上觀察。 View(e); //把第5個數e發送到電腦端的串口助手軟件上觀察。 View(f); //把第6個數f發送到電腦端的串口助手軟件上觀察。 View(g); //把第7個數g發送到電腦端的串口助手軟件上觀察。 while(1) { } } /\*---C語言學習區域的結束。-----------------------------------------------\*/ 在電腦串口助手軟件上觀察到的程序執行現象如下: 開始... 第1個數 十進制:10 十六進制:A 二進制:1010 第2個數 十進制:20 十六進制:14 二進制:10100 第3個數 十進制:4660 十六進制:1234 二進制:1001000110100 第4個數 十進制:85 十六進制:55 二進制:1010101 第5個數 十進制:84 十六進制:54 二進制:1010100 第6個數 十進制:2 十六進制:2 二進制:10 第7個數 十進制:4 十六進制:4 二進制:100 分析: 通過實驗結果,發現在單片機上的計算結果和我們的分析是一致的。 【34.7 如何在單片機上練習本章節C語言程序?】 直接復制前面章節中第十一節的模板程序,練習代碼時只需要更改“C語言學習區域”的代碼就可以了,其它部分的代碼不要動。編譯后,把程序下載進帶串口的51學習板,通過電腦端的串口助手軟件就可以觀察到不同的變量數值,詳細方法請看第十一節內容。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看