你所不知道的C 語言:bitwise 操作 - HackMD
文章推薦指數: 80 %
延伸閱讀: Bitwise Operators in C (連結失效). Uses of Bitwise Operations or Why to Study Bits. Compression; Set operations; Encryption. The Magic of XOR.
ownedthisnote
Published
LinkedwithGitHub
Like8
Bookmark
Subscribe
Edit
---
title:你所不知道的C語言:bitwise操作
image:https://imgur.com/IpzUymh.png
description:複習數值系統,從應用案例引導學員掌握bitwise操作,從而邁向專業的程式開發
tags:DYKC,CLANG,CLANGUAGE
---
#[你所不知道的C語言](https://hackmd.io/@sysprog/c-prog/):bitwise操作
Copyright(**慣C**)2020[宅色夫](http://wiki.csie.ncku.edu.tw/User/jserv)
==[直播錄影](https://youtu.be/S800OmVnN5o)==
##複習數值系統
*短片:[10進位、16進位,和60進位從何而來?](https://www.youtube.com/watch?v=8J7sAYoG50A)
*複習[數值系統篇](https://hackmd.io/@sysprog/c-numerics)
*[解讀計算機編碼](https://hackmd.io/@sysprog/binary-representation)
*[BinaryandNumberRepresentation](https://www.bottomupcs.com/chapter01.xhtml):notes:記得要按右下方的Next
>以下圖例取自[CS:APP第2章重點提示和練習](https://hackmd.io/@sysprog/CSAPP-ch2)
![](https://i.imgur.com/H33tREX.png)
不是所有的位元組合都能表示合理的數字,存取某些位元組合在特定機器上可能會造成嚴重錯誤,此種組合稱作陷阱表示法(traprepresentation)。
除非使用位元運算或是違反標準其他規定(如溢位),一般的運算不可能產生陷阱表示法。
C語言標準明確允許實作自行決定在以下兩種狀況下是否是陷阱表示法:
*型態為有號整數且正負號及值位元為特定組合時(三種格式各有一特殊組合)
*填充位元為某些組合時:notes:[N1256註腳#44,45](http://port70.net/~nsz/c/c99/n1256.html#note44)
![](https://i.imgur.com/DGJUVgk.png)
位元運算會忽略填充位元,因此(等級不低於unsignedint的)無號整數可安心使用。
為求最大可攜性,位元運算不應該用在有號整數上。
在C11規格6.2.6.2Integertypes指出
>Forunsignedintegertypesotherthanunsignedchar,thebitsoftheobjectrepresentationshallbedividedintotwogroups:valuebitsandpaddingbits(thereneednotbeanyofthelatter).IfthereareNvaluebits,eachbitshallrepresentadifferentpowerof2between1and2^N−1^,sothatobjectsofthattypeshallbecapableofrepresentingvaluesfrom0to2^N−1^usingapurebinaryrepresentation;thisshallbeknownasthevaluerepresentation.Thevaluesofanypaddingbitsareunspecified.
`uintN_t`和`intN_t`保證沒有填充位元,`intN_t`一定是二補數,而且`intN_t`不可能有陷阱表示法,堪稱是最安全的整數型態。
實作可能不提供這些型態,不過一旦提供,即保證符合性質。
:notes:[N12567.18.1.1](http://port70.net/~nsz/c/c99/n1256.html#7.18.1.1)p1~p3
位移運算子(Shiftoperator):
*左移:`x<
兩種位移:
*邏輯位移(Logicalshift):左側會補上0
*算術位移(Arithmeticshift):補上號數(signbit)也就是最高有效位元的值在左側
例:
X=10100010;
Logicalshift:`x>>2`=`00101000`
Arithmeticshift:`x>>2`=`11101000`
注意位移運算的兩種未定義狀況
*左移超過變數長度,其結果未定義;
```cpp
inti=0xFFFFFFFF;
i=i<<32;//此結果未定義
```
*右移一個負數時,可能是邏輯位移或是算術位移,C語言標準未定義;
算術位移的應用,若要判斷一個int型態的變數`n`是否為正數,可用`n>>31`其等價於`n>=0?0:-1`.
*右移如果是一個負數時,會變成正或著是負值,要注意編譯器如何實作。
編譯器甚至可以有編譯選項可改變此語意,gcc的實作上是使用arithmeticshift。
![](https://i.imgur.com/HAhmxxQ.png)
有號數x到無號數(U結尾)映射機制:非負數不變為x,負數轉換為大的正數(x+2^w^,最靠近0的負數映射最大,最小的負數被映射為剛好在二補數表達之外的無符號數)。
*例如w=4,二補數能表示的有號數範圍是-8[`1000`]~7[`0111`]
當無號數與有號數在C語言混合在單一表示式時,有號數會被轉換為無號數。
在運算子操作時會有些意外的結果。
有號數負值會因為轉換成2補數後,反而負值會大於正值。
因此要注意混用時的運算狀況。
案例:
|變數1|變數2|比較結果|轉換後|
|------------|-----------------|----|--------|
|0|0U|==|unsigned|
|-1|0||unsigned|
|2147483647|-2147483647-1|>|signed|
|2147483647U|-2147483647-1||signed|
|(unsigned)-1|-2|>|unsigned|
|2147483647|2147483648U||signed|
一個導致意外結果的案例:
```cpp
intn=10;
for(inti=n-1;i-sizeof(char)>=0;i--)
printf("i:0x%x\n",i);
```
這個例子會造成無窮迴圈,因為`sizeof`會回傳值是unsignedint型態,i變數也會被轉換為unsigned的形式,無號數0再減1就會變為`0xFFFFFFFF`而產生無窮迴圈。
![](https://i.imgur.com/G2AXbpz.png)
在有號整數上都可能產生陷阱表示法
補充資訊:[WritingTMininC](http://csapp.cs.cmu.edu/3e/waside/waside-tmin.pdf)
*Figure1列出ISOC90和ISOC99對於資料型態定義的落差
*注意Implications
![](https://i.imgur.com/7YvR10S.png)
延伸閱讀:[SignedandUnsignedNumbers](https://chi_gitbook.gitbooks.io/personal-note/content/signed_and_unsigned_numbers.html)
在[WhirlwindTourofARMAssembly](http://www.coranac.com/tonc/text/asm.htm)指出,若n是有號32-bit整數,那麼`n>>31`相當於`n>=0?0:-1`
:-1:[bitwiseleftshiftoperationinvokesUndefinedBehaviourwhentheleftsideoperandhasnegativevalue](https://stackoverflow.com/questions/3784996/why-does-left-shift-operation-invoke-undefined-behaviour-when-the-left-side-opera)
:-1:[Weirdbehaviorofrightshiftoperator(1>>32)
](https://stackoverflow.com/questions/3394259/weird-behavior-of-right-shift-operator-1-32/3394302#3394302)
若n是32-bit整數,那麼`abs(n)`等同於`((n>>31)^n)-(n>>31)`
*當n是正數時:
>`n>>31`是0;`n^0`仍是n;`n-0`仍是n
*當n是負數時:
>`n>>31`是-1;`-1`以2補數表示為`0xFFFFFFFF`;`n^(-1)`等同於1補數運算;最後再減`-1`,得到2補數運算的值
![](https://i.imgur.com/cWiCbnq.png)
例如:unsignedinttounsignedshort
##BitwiseOperator
*[CBitwiseOperatorsQuiz](http://doc.callmematthi.eu/C_Bitwise_Operators.html)
*[線上測驗](https://pconrad.github.io/old_pconrad_cs16/topics/bitOps/)(附上解答)
*[BitwisePractice](https://web.stanford.edu/class/archive/cs/cs107/cs107.1202/lab1/practice.html)
##做中學
Linux核心原始程式碼存在大量bit(-wise)operations(簡稱`bitops`),頗多乍看像是魔法的C程式碼就是bitops的組合。
這裡舉個案例,觀察下方ASCII字元編號的二進位數值,可知道大寫'A'字元的編號是十進位65,對應於二進位的100_0001(請略去底線,只是為了視覺方便),即2的6次方加1,那麼小寫'a'字母是緊接在大寫'Z'字母之後嘛?不是!小寫'a'字母的編號是十進位97,恰好跟大寫'A'字母相隔32,即2的5次方。
![](https://i.imgur.com/AipH6Qc.png)
為什麼ASCII採用不直覺的設計呢?我們繼續觀察:
*大寫'G'=>71=>0100_0111
*小寫'g'=>103=>0110_0111
從二進位表示法清楚得知,`G`和`g`其實相差1個位元,換言之,透過XOR0010_0000(或C語言的`^32`),就可實作出「大寫字母轉小寫」和「小寫字母轉大寫」的效果。
小提示:在GNU/Linux,FreeBSD,macOS上輸入"manascii"即可得到精美的ASCII表格,有八進位和十進位表示(至於為何UNIX系統和C語言存在八進位表示法,這是另外的故事)。
![](https://i.imgur.com/u1TaJ3s.png)
許多人使用的[vim](https://www.vim.org/)啟發自[BillJoy](https://en.wikipedia.org/wiki/Bill_Joy)在加州大學柏克萊分校就讀時所發展的`vi`編輯器,後者若干特性被[vim](https://www.vim.org/)保留,包含使用==hjkl==等按鍵分別表示游標的向左、向下、向上,及向右。
採用hjkl作為方向鍵的因素是,BillJoy當時採用的ADM-3A鍵盤沒有獨立方向鍵,不過後續的問題是:既然方向鍵和字母按鍵共用,那為何挑==hjkl==,而非asdf一類在[QWERTY鍵盤](https://en.wikipedia.org/wiki/QWERTY)橫向連續的按鍵呢?
當我們回顧1967年ASCII的編碼規範,可發現前32個字元都是控制碼,讓人們得以透過這些特別字元來控制畫面和相關I/O,早期鍵盤的"control"按鍵就搭配這些特別字元使用。
"control"組合按鍵會將原本字元的第1個bit進行XOR,於是`H`字元對應ASCII編碼為100_1000(過去僅用7bit編碼),組合"control"後(即Ctrl+H)會得到000_1000,也就是backspace的編碼,這也是為何在某些程式中按下backspace按鍵會得到`^H`輸出的原因。
相似地,當按下Ctrl+J時會得到000_1010,即linefeed
不只是上述ADM-3A鍵盤如此,當時大部分的電傳打字機(teletyper)也採用相同的慣例。
既然相鄰的H和J都有其對應游標移動的控制碼,也很自然地延伸到K和L按鍵。
###Setabit
:::info
wherenisthebitnumber,and0istheleastsignificantbit
:::
```cpp
unsignedchara|=(1<
*[Jitblt:EfficientRun-timeCodeGenerationforDigitalCompositing](http://vpri.org/pdf/tr2008002_jitblt.pdf)
*[GitHub](https://github.com/damelang/jitblt)
-[]pipeline-1
![](https://i.imgur.com/OdTz4zy.png)
-[]pipeline-2
![](https://i.imgur.com/ZXwTw4J.png)
*新的發展:[Blend2D](https://blend2d.com/)
*2DvectorgraphicsenginewritteninC++.
*Itfeaturesabuilt-inJITcompilerthatgenerateshighperformance2Dpipelinesatruntime,whicharemuchfasterthanstaticpipelinesusedintoday's2Dengines.DynamicpipelineconstructionisthemaindifferencebetweenBlend2Dandother2Dengines,andguaranteesthehighestpossiblethroughtputbytakingadvantageofCPUextensionsdetectedatruntime.
*Blend2Dprovidesalsoadeferredandasynchronousrendering,whichmakesitseamlesstointegratewithevent-basedenvironmentssuchasnode.jsandnextgenerationUItoolkits.
像素處理的程式碼片段:
```cpp
unsignedshortblend(unsignedshortfg,
unsignedshortbg,
unsignedcharalpha){
//Splitforegroundintocomponents
unsignedfg_r=fg>>11;
unsignedfg_g=(fg>>5)&((1u<<6)-1);
unsignedfg_b=fg&((1u<<5)-1);
//Splitbackgroundintocomponents
unsignedbg_r=bg>>11;
unsignedbg_g=(bg>>5)&((1u<<6)-1);
unsignedbg_b=bg&((1u<<5)-1);
//Alphablendcomponents
unsignedout_r=(fg_r*alpha+bg_r*(255-alpha))/255;
unsignedout_g=(fg_g*alpha+bg_g*(255-alpha))/255;
unsignedout_b=(fg_b*alpha+bg_b*(255-alpha))/255;
//Packresult
return(unsignedshort)((out_r<<11)|
(out_g<<5)|
(out_b);
}
```
其中"Alphablendcomponents"可透過改寫為以下:
```cpp
unsignedout_r=fg_r*a+bg_r*(255-alpha);
unsignedout_g=fg_g*a+bg_g*(255-alpha);
unsignedout_b=fg_b*a+bg_b*(255-alpha);
out_r=(out_r+1+(out_r>>8))>>8;
out_g=(out_g+1+(out_g>>8))>>8;
out_b=(out_b+1+(out_b>>8))>>8;
```
參考資料:
[what(r+1+(r>>8))>>8does?-stackoverflow](https://stackoverflow.com/questions/30237567/what-r1-r-8-8-does)
###案例分析
給定每個pixel為32-bit的RGBA的bitmap,其轉換為黑白影像的函式為:
```cpp
voidrgba_to_bw(uint32_t*bitmap,intwidth,intheight,longstride){
for(introw=0;row
雖然BMP缺點是檔案非常大,不過因為沒有壓縮,即使不借助OpenCV、ImageMagick或.NETFramework等等,也可以很容易地直接用StandardCLibrary作影像處理。
BMP主要有四個部份組成:
1.BitmapFileHeader:MagicNumber(’BM’)、filesize、Offsettoimagedata
2.BitmapInfoHeader:imagewidthandheight、thenumberofbitsperpixel、Compressiontype
3.ColorTable(Palette)
4.Imagedata
![](https://hackpad-attachments.s3.amazonaws.com/charles620016.hackpad.com_U4inYKbSPUp_p.431773_1440518623822_Selection_001.bmp)
-[]原始版本
以下是使用一張1920x1080的BMP圖片,所得知的檔案資訊:
```
====Header====
Signature=4D42
FileSize=8294456
DataOffset=54
====Info======
Infosize=40
Width=1920
Height=1080
BitsPerPixel=32
Compression=0
================
RGBAtoBWisinprogress....
Savethepicturesuccessfully!
ExecutiontimeofrgbaToBw():0.034494
```
![](https://hackpad-attachments.s3.amazonaws.com/charles620016.hackpad.com_U4inYKbSPUp_p.431773_1440765925898_01_after.bmp)
:::info
執行時間:0.034494sec
:::
-[]Version1:[LookupTable](https://en.wikipedia.org/wiki/Lookup_table)(LUT)
RGB分別都是8bit,可以建立三個大小為256bytes的table,這樣就不用在每次轉bw過程中進行浮點數運算。
*原本:`bw=(uint32_t)(r*0.299+g*0.587+b*0.114);`
*查表:`bw=(uint32_t)(table_R[r]+table_G[g]+table_B[b]);`
:::info
執行時間:`0.028148sec`
:::
-[]Version2
使用pointer的offset取代原本的繁雜的bitwiseoperation。
```C
uint32_t*pixel=bmp->data;
r=(BYTE*)pixel+2;
g=(BYTE*)pixel+1;
b=(BYTE*)pixel;
```
:::info
執行時間:`0.020379sec`
:::
-[]Version3
將上述Version1和Version2合併在一起
:::info
執行時間:`0.018061sec`
:::
用RaspberryPi2測試:
```shell
CC=gcc-4.8
CFLAGS=-O0-Wall-ftree-vectorize-mcpu=cortex-a7-mfpu=neon-vfpv4-mfloat-abi=hard
```
*Original
*ExecutiontimeofrgbaToBw():0.353600
*Version1:usingRGBtable
*ExecutiontimeofrgbaToBw():0.319600
*Version2:usingpointerarithmetic
*ExecutiontimeofrgbaToBw():0.251800
*Version3:versoin1+versoin2`
*ExecutiontimeofrgbaToBw():0.226800
##案例探討
*[位元旋轉實作和Linux核心案例](https://hackmd.io/@sysprog/bitwise-rotation)
*[revsersebit原理和案例分析](https://hackmd.io/@sysprog/bitwise-reverse)
8
×
Signin
Email
Password
Forgotpassword
or
Byclickingbelow,youagreetoourtermsofservice.
SigninviaFacebook
SigninviaTwitter
SigninviaGitHub
SigninviaDropbox
SigninviaGoogle
NewtoHackMD?Signup
延伸文章資訊
- 1Bitwise Operators in C [With Coding Example] | upGrad blog
- 2Bitwise Operators - Manual - PHP
Bitwise operators allow evaluation and manipulation of specific bits within an integer. ... Examp...
- 3What is bitwise? - Definition from WhatIs.com - TechTarget
- 4Bitwise operation - Wikipedia
In computer programming, a bitwise operation operates on a bit string, a bit array or a binary nu...
- 5Understanding Bitwise Operators - Code
Bitwise operators are operators (just like +, *, &&, etc.) that operate on ints and uints at the ...