資料表示法:位元、位元組與數值

算術位移與邏輯位移

把位元往右位移看似無害——把每個位元往低位端滑一格,某個東西就從底部掉出去——但它藏著一個真正的決定:你在「最高」位帶進什麼新位元?兩種答案給出兩種不同的運算,算術位移和邏輯位移,而選錯會悄悄破壞負數。

「邏輯」右移總是用零填補空出的最高位。它把值當成一袋單純的位元,所以 1000 0000 右移一位變成 0100 0000——乾淨而簡單。「算術」右移則把原本的符號位元複製到最高位,這個技巧叫符號擴展。原因是當你打算把位移當成除法時保留值的符號:把一個二補數負數右移,應該讓它維持為負,並大致減半。所以 8 位元值 1111 1000(即 -8)算術右移一位變成 1111 1100(即 -4)——符號位元被複製進來,而 -8 除以 2 確實是 -4。若改用邏輯位移,同樣的位元會變成 0111 1100,一個大正數,對有號除法而言是錯的。(左移沒有這種分歧;兩者都在低位端補零。)

在 C 裡,規則繫於型別:右移一個「無號」值永遠是邏輯的(補零),而右移一個「有號」值,在實務上於每個常見編譯器都是算術的(符號擴展)——儘管標準在歷史上把負數的有號右移留作實作定義,所以真正可攜的習慣是:當你想要可預測的位元行為時,位移無號值。再加兩個誠實的警告:位移量大於或等於型別寬度,或位移負的量,是未定義行為,而非環繞;而且絕不要假設有號負數的右移與整數除法以相同方式捨入——它捨向負無窮大,而非捨向零。

8 位元 1111 1000 是 -8。算術 >> 1 複製符號位元:1111 1100 = -4(正確的減半)。邏輯 >> 1 補零:0111 1100 = 124(對有號值是錯的)。

算術位移保留符號;邏輯位移總是補零。

位移量大於或等於型別位元寬度(例如 32 位元值位移 32)在 C 裡是未定義行為,而非一個有定義的零或環繞;而且有號值右移捨向負無窮大,不像除法的捨向零。

又称
sign-extending shiftzero-fill shift算術右移邏輯右移