——以O(shè)penCV庫函數(shù)為例"/>

亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        解析“指針對齊”
        ——以O(shè)penCV庫函數(shù)為例

        2019-04-28 05:58:14劉碩
        電子技術(shù)與軟件工程 2019年3期

        文/劉碩

        1 內(nèi)存與指針

        1.1 字節(jié)

        字節(jié)是內(nèi)存的基本單位。一字節(jié)有八位,在內(nèi)存中字節(jié)從上到下按照由低到高的順序編號(如圖1)。

        1.2 字節(jié)在內(nèi)存中的結(jié)構(gòu)

        對于內(nèi)存來說,“數(shù)據(jù)”僅僅是每一個(gè)字節(jié)中的八個(gè)高低電平位的組合;而對于高級語言(如C++)來說,“數(shù)據(jù)”代表的是“對象”。由于對象的“類型”不同,一個(gè)對象儲存在一個(gè)或多個(gè)字節(jié)中。例如在64位系統(tǒng)中,一般情況下char類型對象占1字節(jié),而int類型的對象要占4個(gè)字節(jié)。在C++中,讀取一個(gè)T類型對象在內(nèi)存中占多少個(gè)字節(jié)是通過sizeof(T)完成的(如前所述可以得出sizeof(int)等于4)。

        1.3 C++中的指針

        指針是對象在內(nèi)存中的地址,它的值是對應(yīng)字節(jié)的編號。如果我們在C++中定義一個(gè)指向T類型對象的指針P(為了避免對指針的操作和對指針值的操作混淆,我們把指針P的值記為valueP)。那么P的含義是“內(nèi)存中第valueP個(gè)字節(jié)開始,到第valueP+sizeof(T)個(gè)字節(jié),這一段連續(xù)的內(nèi)存中保存著一個(gè)T類型的對象”。

        由此我們得到第一個(gè)結(jié)論:對于內(nèi)存來說,數(shù)據(jù)的長度是統(tǒng)一的,始終是一字節(jié);對于高級語言來說,對象的長度是根據(jù)對象的類型(如char、int)變化的,是sizeof(T)個(gè)字節(jié)。

        “在某些架構(gòu)下,從一個(gè)不被對象大小均勻分割的地址中讀取多字節(jié)對象是不可能(比如從32位整形中讀取4比特)。在像x86這樣的架構(gòu)下,CPU通過多次讀取并從這些讀取中獲取你的值來自動(dòng)處理這種情況,但代價(jià)是顯著降低了性能”——《學(xué)習(xí)OpenCV3(中文版)》為了提高效率,有必要對申請內(nèi)存產(chǎn)生的原始指針進(jìn)行處理,使指針的值能被對象長度整除(如圖2)。

        1.4 讀取內(nèi)存中的字節(jié)

        根據(jù)指針與字節(jié)編號的關(guān)系,我們可以很自然的想到:在解引用指針時(shí),指針?biāo)笇ο蟮念愋鸵?guī)定了本次解引用需要要一次讀取幾個(gè)內(nèi)存單元(字節(jié))。比如在解引用char*類型的指針時(shí)需要讀取一個(gè)字節(jié)的數(shù)據(jù),而解引用int*類型指針時(shí),需要一次讀取四個(gè)字節(jié)的數(shù)據(jù)。由此產(chǎn)生了一個(gè)不容忽視的問題——指針類型轉(zhuǎn)換時(shí),類型大小的問題。

        ·較高的指針類型轉(zhuǎn)換成較低的指針類型(int* -> char*):這種轉(zhuǎn)換是安全的。只不過這樣解引用指針時(shí),讀取的字節(jié)數(shù)會(huì)由原來的四個(gè)轉(zhuǎn)變成一個(gè)。

        ·較低的指針類型轉(zhuǎn)換成較高的指針類型(char* ->int*):這種轉(zhuǎn)換是危險(xiǎn)的。因?yàn)檫@樣解引用指針的時(shí)候,讀取的字節(jié)個(gè)數(shù)會(huì)由原來的一個(gè),轉(zhuǎn)變成四個(gè)。我們不能保證多被讀取的字節(jié)中是否存儲著有其他用途的數(shù)據(jù),對這樣得到的內(nèi)存加以修改,很可能引發(fā)程序運(yùn)行異常。

        2 內(nèi)存分配與指針對齊

        2.1 申請一段連續(xù)的內(nèi)存

        在C/C++環(huán)境下使用動(dòng)態(tài)內(nèi)存分配時(shí),malloc()函數(shù)返回的值是申請到的內(nèi)存資源中,第一個(gè)字節(jié)的編號,即指向一段連續(xù)的內(nèi)存資源首地址的指針P。如果我們用申請得到的內(nèi)存資源來存放n個(gè)T類型的對象,不能保證valueP可以被sizeof(T)整除。由此我們需要進(jìn)行“指針對齊”

        2.2 將指針對齊

        指針對齊操作alignPtr()

        (T*)(((size_t)P + n-1) & -n);

        · P是malloc()返回的指針,即得到的內(nèi)存資源首地址,也是需要進(jìn)行對齊操作的指針。

        · T是我們要存放的數(shù)據(jù)的類型(顯然T*就是ptr的類型)

        · n是T類型數(shù)據(jù)所占的字節(jié)數(shù)

        圖1

        ·“(size_t)P”這個(gè)表達(dá)式的值就是P指針的值,即valueP

        ·表達(dá)式的結(jié)果就是對齊之后的指針

        ·OpenCV源碼:

        template static inline _Tp*alignPtr(

        _Tp* ptr, intn=(int)sizeof(_Tp)){

        CV_DbgAssert((n & (n - 1)) == 0); // n is a power of 2

        return (_Tp*)(((size_t)ptr + n-1) & -n);}

        情況一:分配的內(nèi)存第一個(gè)字節(jié)的編號(分配內(nèi)存首地址)是對象長度的整數(shù)倍。

        假設(shè)編號(首地址)為0000 0100 B ,要在這段內(nèi)存中存放一些長度為4的對象。則(size_t)P + n-1)等于:

        0000 0100 B

        +0000 0100 B

        -0000 0001 B

        =0000 0111 B

        當(dāng)n=0000 0100 B時(shí),-n是1111 1100 B(取反加一),于是(size_t)P + n-1)&-n就是低二位置零,使得0000 0111 B變成0000 0100 B.

        對比觀察得到在這種情況下,表達(dá)式的值即P的值。

        情況二:分配的內(nèi)存第一個(gè)字節(jié)的編號(分配內(nèi)存首地址)不是是對象長度的整數(shù)倍。

        假設(shè)編號(首地址)為0000 0110 B,要在這段內(nèi)存中存放一些長度為4的對象,則(size_t)ptr + n-1)等于:

        0000 0110 B

        +0000 0100 B

        -0000 0001 B

        =0000 1001 B

        當(dāng)n=0000 0100 B時(shí),-n是1111 1100 B(取反加一),于是(size_t)P + n-1)&-n就是去掉低二位,使得0000 1001 B變成0000 1000 B.

        對比觀察可以發(fā)現(xiàn)這種情況下,表達(dá)式的值是P的值加一個(gè)不大于n的整數(shù),且表達(dá)式的值可以被n整除。由“不可整除”到“可以整除”這個(gè)過程叫做“對齊”

        圖2:一個(gè)T類型占4個(gè)字節(jié),打√的編號可以被4整除

        圖3

        通過“對齊表達(dá)式”得到的指針?biāo)傅膬?nèi)存,是我們真正寫入數(shù)據(jù)的起點(diǎn),而我們申請的內(nèi)存是從P開始分配的。釋放內(nèi)存時(shí),也應(yīng)該從P開始釋放,如何保證P和表達(dá)式的值之間的字節(jié)能夠被釋放呢?只需要在調(diào)用malloc()函數(shù)時(shí),多申請sizeof(void *)個(gè)字節(jié),之后將P存入這幾個(gè)字節(jié)即可,銷毀時(shí)把P從這幾個(gè)字節(jié)中讀取出來供free()函數(shù)使用。

        2.3 將數(shù)據(jù)寫入內(nèi)存

        假設(shè)有X個(gè)T類型對象需要寫入內(nèi)存,每個(gè)對象長度是sizeof(T),那么調(diào)用malloc()時(shí),應(yīng)該是malloc(sizeof(T)*x+sizeof(void*)),這樣得到的字節(jié)數(shù)量正好是x個(gè)對象和一個(gè)指針需要的空間,但是因?yàn)橹羔槍R時(shí)是會(huì)舍棄幾個(gè)字節(jié)不用的,這幾個(gè)字節(jié)的數(shù)量大于零且小于對象的長度,所以還要再多申請存放一個(gè)對象所需要的字節(jié),以補(bǔ)充因舍棄而減少的內(nèi)存空間。故調(diào)用malloc()時(shí),參數(shù)為

        Sizeof(T)*x+sizeof(void*)+sizeof(T)

        *從用”sizeof(void*)”來預(yù)留一個(gè)指針?biāo)枰淖止?jié)數(shù)來看,指針的長度始終是固定的,而指針?biāo)赶虻膶ο箝L度是隨著對象的類型變化的。

        3 實(shí)例應(yīng)用

        OpenCV源碼:

        ·udata是調(diào)用malloc()之后得到的資源中的第一個(gè)字節(jié)編號。是一個(gè)指向資源首地址的指針,是一個(gè)未對齊的指針

        ·將幾個(gè)T類型的對象放入這片連續(xù)的內(nèi)存,每個(gè)T類型的對象所在的地址都被一個(gè)指針?biāo)浮_@些指針組成了一個(gè)指針數(shù)組adata。第一個(gè)T類型對象的地址是adata[0],第二個(gè)T類型對象的地址是adata[1]...以此類推。在我們想用T類型對象的時(shí)候可以解引用指針 *adata[index]。此外還可以用adata[-1]這個(gè)位置存儲udata的值,以便銷毀時(shí)利用。

        ·adata的值應(yīng)該是用udata對齊后的值。因?yàn)閡data是uchar*類型,而adata是uchar**類型(C++中數(shù)組名自動(dòng)轉(zhuǎn)換成指向數(shù)組首地址的指針),直接帶入alignPtr()中進(jìn)行對齊會(huì)有類型錯(cuò)誤,所以需要強(qiáng)制類型轉(zhuǎn)換以滿足利用udata產(chǎn)生adata。強(qiáng)制轉(zhuǎn)換不會(huì)改變udata的值、長度。

        執(zhí)行fastMalloc()之后會(huì)返回adata,這時(shí)內(nèi)存與指針的關(guān)系如圖3所示。

        根據(jù)剛剛的分析udata和pdata之間有幾個(gè)(或者沒有)字節(jié)被閑置,不能保證是否為adata[-1]提供了足夠的字節(jié)以存放udata。因此在調(diào)用alignPtr()時(shí),傳入的第一個(gè)參數(shù)是(uchar**)udata + 1?!?uchar**)”優(yōu)先級比“+”要高,所以這個(gè)操作是“對指向指針的指針加一”?!爸羔樇右弧钡牟僮鞅硎尽爸羔槷?dāng)前值+指針?biāo)傅膶ο笏嫉淖止?jié)數(shù)”。就是說指針對齊的時(shí)候,給udata預(yù)留了sizeof(void *)個(gè)字節(jié),保證adata前面至少有存放一個(gè)指針的空間。

        4 關(guān)于指針的性質(zhì)

        4.1 指針的長度

        根據(jù)計(jì)算機(jī)CPU的架構(gòu)而定,簡單說,32位計(jì)算機(jī)的指針是32個(gè)bit組成,也就是4字節(jié);64位計(jì)算機(jī)的指針長度是64bit組成,也就是8字節(jié)。

        4.2 指針的操作

        “指針加一”和“指針的值”加一是兩種情況。假設(shè)有一個(gè)指向T類型對象的指針P,P的值是valueP。P+1這個(gè)操作等價(jià)于valueP+sizeof(T)。而valueP+1是令P指針指向“當(dāng)前所指的字節(jié)的”下一個(gè)字節(jié)。

        av毛片一区二区少妇颜射| 国产亚洲精品资源在线26u| 国产思思99re99在线观看| 亚洲AV无码日韩综合欧亚| 综合激情五月三开心五月| 三年片免费观看影视大全视频| 欧美黑人粗暴多交高潮水最多| 极品诱惑一区二区三区| 亚洲中文字幕精品久久久 | a级毛片免费观看在线播放| 国产精品亚洲综合色区韩国| 国产精品国产三级国产专播| 白白色日韩免费在线观看| 亚洲国产日韩a在线乱码| 大肉大捧一进一出好爽视色大师| 国产无套视频在线观看香蕉| 久久亚洲精品一区二区| 91精品国产92久久久| 亚洲av无码之国产精品网址蜜芽| Y111111国产精品久久久| 亚洲大胆美女人体一二三区| 国产在线第一区二区三区| 黑人巨大白妞出浆| 国产成人久久精品77777综合| 亚洲一区二区蜜桃视频| 777米奇色狠狠俺去啦| 亚洲福利视频一区 | 天天综合网网欲色| 欲色天天网综合久久| WWW拍拍拍| 国产高清精品一区二区| 一本色道无码道dvd在线观看| 欧美a级在线现免费观看| 日本97色视频日本熟妇视频 | 亚洲VA中文字幕无码毛片春药| 97超碰国产一区二区三区| 日本一区二区三区爆乳| 色婷婷五月综合亚洲小说| 国产亚洲精品综合99久久| 国产av剧情一区二区三区| 老外和中国女人毛片免费视频|