在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

周立功教你學程序設計結構體:內存對齊和基本數據類型

AGk5_ZLG_zhiyua ? 來源:未知 ? 作者:電子大兵 ? 2017-09-01 13:47 ? 次閱讀

第二章為程序設計技術,本文為2.2.1 內存對齊和2.2.2 基本數據類型

我們知道,數組和指針是相同類型有序數據的集合,但很多時候需要將不同類型的數據捆綁在一起作為一個整體來對待,使程序設計更方便。在C語言中,這樣的一組數據被稱為結構體。

>>>2.2.1內存對齊

雖然所有的變量最后都會保存到特定地址的內存中,但相應的內存空間必須滿足內存對齊的要求。主要出于兩個方面的原因:

  • 平臺原因:不是所有的硬件平臺(特別是嵌入式系統中使用的低端微處理器)都能訪問任意地址上的任意數據,某些硬件平臺只能訪問對齊的地址,否則會出現硬件異常。

  • 性能原因:如果數據存放在未對齊的內存空間中,則處理器訪問變量時需要做兩次內存訪問,而對齊的內存訪問僅需要一次訪問。

在32位微處理器中,處理器訪問內存都是按照32位進行的,即一次讀取或寫入都是4個字節,比如,地址0x0 ~ 0xF這16字節的內存,對于微處理器來說,不是將其看作16個單一字節,而是4個塊,每塊4個字節,詳見圖2.4。

圖2.4 內存空間示意圖

顯然,只能從0x0、0x4、0x8、0xC等地址為4的整數倍的內存中一次取出4個字節,并不能從任意地址開始一次讀取4個字節。假定將一個占用4字節的int類型數據存放到地址0開始的4字節內存中,其示意圖詳見圖2.5。

圖2.5 按內存對齊的方式存儲int數據

由于int類型數據存放在塊0中,因此CPU僅需一次內存訪問即可完成對該數據的讀取或寫入。反之,如果將該int類型數據存放在地址1開始的4字節內存空間中,其示意圖詳見圖2.6。

圖2.6 按內存未對齊的方式存儲int數據

此時,數據存放在塊0和塊1兩個塊中,若要完成對該數據的訪問,必須經過兩次內存訪問,先通過訪問塊0得到該數據的3個字節,再通過訪問塊1得到該數據的1個字節,最后通過運算,將這幾個字節合并為一個完整的int型數據。由此可見,若數據存儲在未對齊的內存空間中,將大大降低CPU的效率。但在某些特定的微處理器中,它根本不愿意干這種事情,這種情況下,就出現系統異常,直接崩潰了。內存對齊的具體規則如下:

(1)結構體各個成員變量的內存空間的首地址必須是“對齊系數”和“變量實際長度”中較小者的整數倍。假設要求變量的內存空間按照4字節對齊,則內存空間的首地址必須是4的整數倍,滿足條件的地址有0x0、0x4、0x8、0xC……

(2)對于結構體,在其各個數據成員都完成對齊后,結構體本身也需要對齊,即結構體占用的總大小應該為“對齊系數”和“最大數據成員長度” 中較小值的整數倍。

一般來說,對齊系數與微處理器的字長相同,比如,32位微處理器的對齊系數是4字節,變量的實際長度與其類型相關,計算類型長度的方法如下:

該程序的輸出為:1、4、4、4、8。假定CPU為32位微處理器,對齊系數為4,結構體變量data的定義如下:

結構體的各個成員都是從結構體首地址(其由編譯器保證必然滿足內存對齊的要求,假定為0)開始計算,按照定義的順序依次存放各個成員,詳見表2.1。

表2.1依次存放各個成員

實際存放位置使用[x,y]表示,x表示起始地址,y表示結束地址。如果x與y相等,則直接使用[x]表示。以成員b為例,其長度為2,小于對齊系數,因此按照2字節對齊,就要求其地址必須是2的倍數,地址0已經被成員a占用,則只能使用滿足要求的鄰近的內存空間[2,3]存放成員b。而空間[1]由于不滿足存放成員b的要求,則只能被棄用。特別地,對于數組成員c,存放時不能將其看作一個整體,即長度為2的成員,應該分別看作兩個成員c[0]和c[1]。由此可見,實際存放位置為[0,24],1、6、7、17、18、19部分內存空間被棄用。

當所有成員存放完畢后,則結構體本身也需要對齊,即結構體的大小也應該為對齊字節數的整數倍,對齊字節數取長度最長的成員和“對齊系數”的較小值。在這里,其長度最長的成員為double類型的成員d,其長度為8,大于對齊系數,因此結構體本身也要按照4字節對齊,其占用的空間大小必須是4的整數倍。雖然當前存放位置為[0,24],只占用了25個字節。由于必須滿足4的整數倍,因此實際上結構體占用的空間是28個字節,即[0,27]。驗證結構體占用空間大小的方法如下:

雖然所有成員的總長度為19個字節,但結構體實際占用了28個字節,多余的9個字節空間為內存對齊棄用的空間,即1、6、7、17、18、19、25、26、27,分為4個段:[1],[6,7],[17,19],[25,27]。查看表2.1可知,這些浪費空間的前面,存放的都是char型數據,由于char型數據只占用一個字節,往往使得其緊接著的空間不能被其它長度更長的數據使用。

為了降低內存浪費的概率,應該在char型數據之后,存放長度最小的成員。即在定義結構體時,應按照長度遞增的順序依次定義各個成員。優化示例結構體的定義如下:

類似地,依次存放各個成員,詳見表2.2。

表2.2依次存放各個成員

所有成員實際存放位置為[0,19],中間的地址為5的內存空間被棄用。由于結構體占用的大小為20個字節,已經是4的整數倍,因此無需再做額外的處理。結構體只浪費了1個字節空間,使用率達到95%。顯然,通過優化結構體成員的定義順序,在同樣滿足內存對齊的要求下,可以大大地減少內存的浪費。

>>>2.2.2基本數據類型

1.范圍值校驗

如果有min≤value≤max,則check()范圍值校驗函數需要3個int型參數value、min和max。如果value合法,則返回true,否則返回false,詳見程序清單2.10。

程序清單 2.10 rangeCheck()范圍值校驗函數的實現(1)

  • 代碼整潔之道

rangeCheck是一個非常具有描述性的名字,因為它較好地描述了函數要做的事,所以好名字的價值怎么評價都不過分。如果每個示例都讓你感到深合己意,那就是整潔代碼。函數越短小,功能越集中,就越容易取一個好名字。名字長一些并不可怕,長而具有描述性的名字,比短而令人費解的名字更好。選擇具有描述性的名字能幫助程序員理清模塊的設計思路,追索好名字往往會使代碼重構得更好。

從代碼整潔之道的角度來看,最理想的函數參數個數是0(零參數函數),其次是單參數函數,再次是雙參數函數,因盡量避免三參數函數。如果需要三個以上的參數,需要有足夠的理由,否則無論如何也不要這樣做,因為參數帶有太多的概念性。

從測試的角度來看,參數甚至更叫人感到為難,因為編寫確保參數的各種組合運行正常的測試用例,且測試覆蓋所有可能值的組合是令人生畏的事情。輸出參數比輸入參數還要難以理解,因為人們習慣性地認為,信息通過參數輸入函數,通過返回值從函數中輸出,輸出參數往往讓人苦思之后才會覺得恍然大悟。如果函數看起來需要兩個、三個或三個以上的參數,說明其中的一些參數就應該封裝為結構體類。比如:

由此可見,減少函數參數的最佳方法是一個函數只做一件事,“函數要么做什么事,要么回答什么事!”兩者不可兼得。函數應該修改某個對象的狀態,或返回該對象的有關信息,兩樣都干常常會出現混亂。

2.類型與變量

由于有了結構體,因此可以將rangeCheck()的形參min和max轉移到結構體中,不僅減少了一個形參,而且處理起來更方便。比如:

該聲明描述了一個由兩個int類型變量組成的結構體,不僅創建了實際數據的對象range,而且描述了該對象是由什么組成的,因為它勾勒出了結構體是如何存儲數據的。顯然,range是struct _Range類型的結構體變量,如果在該結構體定義前添加typedef:

此時,range就變成了該結構體的類型,即range等同于struct _Range。習慣的寫法是將類型名的首字符大寫,將變量名的首字符小寫。有了Range類型,即可同時定義一個Range類型的變量range和一個指向Range *類型的指針變量pRange,當然也可以省略類型名_Range。比如:

注意,結構體有兩層含義,一層含義是“結構體布局”,結構體布局告訴編譯器是如何表示數據的,但它并未讓編譯器為數據分配空間。下一步是創建一個結構體變量,即結構體的另一層含義,其定義如下:

編譯器執行這行代碼便創建了一個結構體變量range,編譯器使用Range為該變量分配空間:一個int類型的變量min和一個int類型的變量max,這些存儲空間都與一個名稱range結合在一起。

3.初始化

假設value值的有效范圍為0~9,在這里可以使用名為newRangeCheck的宏方便地將結構體初始化。比如:

使用方法如下:

宏展開后如下:

其相當于:

從本質上來看,.min和.max的作用相當于Range結構體的下標。雖然Range是一個結構體,但range.min和range.max都是int類型的變量,因此可以象使用其它int類型變量那樣使用它,比如,&(range.min)。

由此可見,如果初始化一個靜態存儲期的結構體,初始化列表中的值必須是常量表達式。如果是自動存儲期,初始化列表中的值可以不是常量。

4.接口與實現

(1)傳遞結構體成員

只要結構體成員是一個具有單個值的數據類型,比如,int、char、float、double或指針,便可將它作為參數傳遞給接受該特定類型的函數,rangeCheck()的實現詳見程序清單2.11。

程序清單 2.11 rangeCheck()函數的實現(2)

其調用形式如下:

rangeCheck()既不知道也不關心實參是否是結構體的成員,它只要求傳入的數據是int類型。如果需要在被調函數中修改主調函數中成員的值,就要傳遞成員的地址。

(2)傳遞結構體

雖然傳遞一個結構體比一個單獨的值復雜,但標準C同樣允許將結構體作為參數使用,rangeCheck()函數的實現詳見程序清單2.11。

程序清單 2.12 rangeCheck()函數的實現(3)

其調用形式如下:

雖然通過這種方法能夠得到正確的結果,但它的效率很低,因為C語言的參數傳址調用方式要求將參數的一份拷貝傳遞給函數。假設結構體的成員是一個占用128字節的數組,甚至更大的數組。如果要將它作為參數進行傳遞,則必須將所占用的字節數復制到堆棧中,以后再丟棄。

(3)傳遞結構體的地址

假設有一組這樣的數據,存儲在結構體成員數組中。其數據結構如下:

顯然,只要將結構體的地址(int *)&st作為實參傳遞給iMax()的形參,即可求出數組中元素的最大值,詳見程序清單 2.13。

程序清單 2.13 求數組中元素的最大值范例程序

下面還是以范圍值校驗器為例,定義一個指向該結構體的指針變量pRange,其初始化、賦值與普通指針變量是一樣的:

和數組不一樣,結構名并不是結構體的地址,因此要在結構名前加上&運算符,因此這里的pRange為指向Range結構體變量range的指針變量。雖然pRange、&range和&range.min的類型不一樣,但它們的值相等,那么下面的關系恒成立:

由于.運算符比*運算符的優先級高,因此必須使用圓括號。這里著重理解pRange是一個指針,pRange->min表示pRange指向結構體的首成員,所以pRange->min是一個int類型的變量,rangeCheck()函數的實現詳見程序清單2.14。

程序清單 2.14 rangeCheck()函數的實現(4)

rangeCheck()使用指向Range的指針pRange作為它的參數,將地址&range傳遞給該函數,使得指針pRange指向range,然后通過->運算符獲取range.min和range.max的值。注意,必須使用&運算符獲取結構體的地址,和數組名不同,結構體名只是其地址的別名。

其調用形式如下:

(4)用函數指針調用

如果需要增加一個奇偶校驗器對value值進行偶校驗,其數據結構如下:

oddEvenCheck()函數的實現詳見程序清單 2.15。

程序清單 2.15 oddEvenCheck()函數的實現

當系統需要多個校驗器后,在運行時調用者將根據實際情況決定調用哪個函數,根據依賴倒置原則,最好的方法是用函數指針隔離變化。無論什么校驗器,其相同的處理部分是value值的合法性判斷,因此將其抽象為模塊。而可變的是value值和校驗參數,由外部傳入的參數應對。由于各種校驗器的類型不一樣,因此必須使用“void *pData”作為形參才能接受任意類型的數據,即將Range *pRange和OddEven*pOddEven泛化成了void *pData。Validate類型的定義如下:

其中,pData為指向任意校驗器參數的指針,value為待校驗的值,通用校驗器的接口詳見程序清單 2.16。

程序清單 2.16 通用校驗器接口(validator.h)

以范圍值校驗器為例,其調用形式如下:

這次傳遞給函數的是一個指向結構體的指針,指針比整個結構體要小得多,所以將它壓到堆棧上的效率要高很多,validator接口的實現詳見程序清單 2.17。

程序清單 2.17 validator接口的實現(validator.c)

由于pRange、pOddEven與pData的類型不同,因此需要對pData強制類型轉換,才能引用相應結構體的成員。注意,在這里,作者并沒有提供完整的代碼,請讀者補充完善。


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • C語言編程
    +關注

    關注

    6

    文章

    90

    瀏覽量

    21108
  • 程序設計
    +關注

    關注

    3

    文章

    261

    瀏覽量

    30398
  • 周立功
    +關注

    關注

    38

    文章

    130

    瀏覽量

    37656
  • 結構體
    +關注

    關注

    1

    文章

    130

    瀏覽量

    10848

原文標題:周立功:結構體使你的程序設計更方便——內存對齊和基本數據類型

文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    程序設計數據結構立功數十年心血力作

    為了將實際開發過程中總結的有價值的技術應用分享給大家,立功及其團隊整理出《程序設計數據結構》這本書,其內容如同培訓講師的教案,是
    發表于 05-26 10:06 ?29次閱讀

    立功程序設計數據結構”:深度解剖動態分布內存的free()函數與realloc()函數

    立功教授數年之心血之作《程序設計數據結構》,書本內容公開后,在電子行業掀起一片學習熱潮。
    的頭像 發表于 08-25 14:22 ?1.5w次閱讀

    立功教你C語言編程與程序設計:這樣寫函數指針數組最好用

    立功教授數年之心血之作《程序設計數據結構》以及《面向AMetal框架與接口的編程(上)》,電子版已無償性分享到電子工程師與高校群體,在公眾號回復【編程】即可在線閱讀。
    的頭像 發表于 08-31 14:06 ?6912次閱讀
    <b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>學</b>C語言編程與<b class='flag-5'>程序設計</b>:這樣寫函數指針數組最好用

    C語言中的基本數據類型

    C語言是非常重要的一門程序設計語言,學好C語言再去學習其他編程語言將變得很輕松,因為大部分語言的都有相同的共性存在。本節我們來講解一下C語言中的基本數據類型,掌握如何使用變量以及變量在編程中的作用及重要性,學會區分變量的存儲類型
    發表于 04-26 17:33 ?2024次閱讀
    C語言中的基<b class='flag-5'>本數據類型</b>

    新書創作談:立功教授數十年之心血力作《程序設計數據結構

    ` 近日,立功教授公開了數十年之心血力作《程序設計數據結構》,此書在4月28日落筆,電子版已無償性分享到電子工程師與高校群體,在致遠電子公眾號后臺回復關鍵字【
    發表于 05-15 18:04

    【完整資料】《程序設計數據結構立功數十年心血力作

    ,是立功和團隊的讀書筆記和程序設計實踐的心得?!?b class='flag-5'>程序設計與數據結構》重點闡述了三大方向內容。C語言學習中的痛點:針對當前工程師在C語言學習
    發表于 05-16 16:43

    C預處理與C語言基本數據類型

    指令表:注意:宏名的書寫由標識符與兩邊各兩條下劃線構成。C語言基本數據類型不同操作系統中數據類型所占字節數圖解數據類型的其他分類:變量常量(字面量和const常量)void(特殊類型
    發表于 12-21 08:29

    Java的基本數據類型與條件結構

    《Java基礎入門》第二篇1 基本數據類型,運算符與表達式,條件結構,循環結構...
    發表于 12-23 08:02

    μCOS-II程序設計基礎_立功公司編著

    μCOS-II程序設計基礎 立功公司編著
    發表于 12-28 15:01 ?13次下載

    C語言教程之C語言基本數據類型與順序程序設計講解

    本文檔的主要內容詳細介紹的是C語言教程之C語言基本數據類型與順序程序設計講解。在程序運行時,其值不能被改變的量稱為常量。常量可分為三種:整型常量,實型常量,字符型常量,符號常量
    發表于 10-26 16:48 ?3次下載

    C語言程序設計教程之基本數據類型、運算符和表達式的詳細資料概述

    本文檔的主要內容詳細介紹的是C語言程序設計教程之基本數據類型、運算符和表達式的詳細資料概述主要內容包括了:1 C語言的數據類型 2 常量和變量3 整型數據 4 實型
    發表于 10-31 18:04 ?30次下載
    C語言<b class='flag-5'>程序設計</b>教程之基<b class='flag-5'>本數據類型</b>、運算符和表達式的詳細資料概述

    C語言程序設計教程之基本數據類型和運算符及表達式的資料說明

    本文檔詳細介紹的是C語言程序設計教程之基本數據類型和運算符及表達式的資料說明主要內容包括了:1 C的數據類型,2 常量與變量,3 數據類型的轉換,4 算術運算符,5 賦值運算,6 關系
    發表于 01-25 15:44 ?0次下載
    C語言<b class='flag-5'>程序設計</b>教程之基<b class='flag-5'>本數據類型</b>和運算符及表達式的資料說明

    51單片機學習筆記(9)——C51的基本數據類型和擴充數據類型

    本數據類型:擴充數據類型
    發表于 11-14 13:36 ?1次下載
    51單片機學習筆記(9)——C51的基<b class='flag-5'>本數據類型</b>和擴充<b class='flag-5'>數據類型</b>

    Struct結構數據類型

    Struct類型是一種由多個不同數據類型元素組成的數據結構,其元素可以是基本數據類型,也可以是Struct、數組等復雜數據類型以及PLC
    的頭像 發表于 07-25 17:02 ?3031次閱讀

    本數據類型分享

    本數據類型本數據類型:包括位、位序列、整數、浮點數、日期時間。此外字符也屬于基本數據類型,請參見文檔String與WString。 1.位和位序列 2.整數數據類型 3.浮點型實
    的頭像 發表于 06-13 14:14 ?1w次閱讀
    基<b class='flag-5'>本數據類型</b>分享
    主站蜘蛛池模板: 黄色免费看视频| 欧美午夜视频一区二区| 激情六月网| 久久免费99精品久久久久久| 久青草视频在线播放| 久久精品韩国三级| 国产亚洲精品成人a在线| 成人永久免费视频网站在线观看| jiuse视频| 天天操天天干天天射| 久久精品国产精品亚洲红杏| 波多野结衣三个女人蕾丝边| 国产h在线| av手机在线播放| 婷婷丁香综合网| 欧美日韩啪啪| 国产人成精品免费视频| 亚洲精品视频在线| 国产综合13p| 亚洲专区一区| 亚洲国产香蕉视频欧美| 日本媚薬痉挛在线观看免费| 狠狠色噜噜狠狠狠狠奇米777| 1024成人| 国产情侣草莓视频在线| 亚洲欧美视频网站| 亚洲国产成人在线| 你懂的网站在线播放| 爆操极品美女| 久久狠狠躁免费观看| 又粗又大又爽又色又过瘾视频| 伊人久久影院大香线蕉| 日本吻胸抓胸激烈视频网站| 国产一区二区播放| 天天操夜夜操美女| 狠狠躁夜夜躁人人爽天天miya| 最新人妖shemaletube人妖| 三级视频网| 成人欧美一区二区三区| 免费操人视频| 清冷双性被cao的合不拢腿|