• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

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

      2019-04-28 05:58:14劉碩
      電子技術(shù)與軟件工程 2019年3期
      關(guān)鍵詞:指針表達式字節(jié)

      文/劉碩

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

      1.3 C++中的指針

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

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

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

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

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

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

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

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

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

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

      2.2 將指針對齊

      指針對齊操作alignPtr()

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

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

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

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

      圖1

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

      ·表達式的結(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)存第一個字節(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時,-n是1111 1100 B(取反加一),于是(size_t)P + n-1)&-n就是低二位置零,使得0000 0111 B變成0000 0100 B.

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

      情況二:分配的內(nèi)存第一個字節(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時,-n是1111 1100 B(取反加一),于是(size_t)P + n-1)&-n就是去掉低二位,使得0000 1001 B變成0000 1000 B.

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

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

      圖3

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

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

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

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

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

      3 實例應(yīng)用

      OpenCV源碼:

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

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

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

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

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

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

      4.1 指針的長度

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

      4.2 指針的操作

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

      猜你喜歡
      指針表達式字節(jié)
      No.8 字節(jié)跳動將推出獨立出口電商APP
      一個混合核Hilbert型積分不等式及其算子范數(shù)表達式
      表達式轉(zhuǎn)換及求值探析
      No.10 “字節(jié)跳動手機”要來了?
      偷指針的人
      娃娃畫報(2019年5期)2019-06-17 16:58:10
      淺析C語言運算符及表達式的教學(xué)誤區(qū)
      為什么表的指針都按照順時針方向轉(zhuǎn)動
      簡談MC7字節(jié)碼
      基于改進Hough變換和BP網(wǎng)絡(luò)的指針儀表識別
      電測與儀表(2015年5期)2015-04-09 11:30:42
      ARM Cortex—MO/MO+單片機的指針變量替換方法
      定结县| 名山县| 扬中市| 营口市| 两当县| 六安市| 措勤县| 柘荣县| 西城区| 越西县| 光泽县| 开封县| 夏邑县| 麻江县| 铜山县| 高陵县| 锦州市| 邯郸县| 逊克县| 福鼎市| 玛曲县| 昌宁县| 咸阳市| 杂多县| 彰化市| 沙坪坝区| 云南省| 类乌齐县| 浮梁县| 塘沽区| 宁夏| 屏东市| 静宁县| 安丘市| 肇源县| 基隆市| 郁南县| 平泉县| 通许县| 巴塘县| 辉南县|