字節對齊&內存管理

  用C語言寫程序時需要知道是大端模式還是小端模式。

         所謂的大端模式,是指數據的低位保存在內存的高地址中,而數據的高位,保存在內存的低地址中;所謂的端模,是指數據的低位保存在內存的低地址中,而數據的高位保存在內存的高地址中

 

         爲什麼會有大小端模式之分呢?這是因爲在計算機系統中,我們是以字節爲單位的,每個地址單元都對應着一個字節,一個字節爲8bit。但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對於位數大於8位的處理器,例如16位或者32位的處理器,由於寄存器寬度大於一個字節,那麼必然存在着一個如果將多個字節安排的問題。因此就導致了大端存儲模式和小端存儲模式。例如一個16bit的short型x,在內存中的地址爲0x0010,x的值爲0x1122,那麼0x11爲高字節,0x22爲低字節。對於大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x2211中小端模式,剛好相反,還是ox1122。我們常用的X86結構是小端模式,而KEIL C51則爲大端模式。很多的ARM,DSP都爲小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。

 

        Java、.NET大行其道的今天,C語言作爲一門經典的高級語言,存在的唯一理由就是其高效、精練。隨着PC硬件升級和降價,C語言由於其自身的複雜度,在開發PC應用軟件時,已經很少使用。但是在開發嵌入式系統軟件和操作系統時,由於普遍強調微內核,少佔用空間和高效,因此,在系統開發舞臺上,C語言依舊是主角

      在實際的程序開發中,爲了提高數據的讀取效率,在內存資源足夠的情況下,一般在定義數據結構時,應該考慮四字節對齊,其原因很簡單,現在的計算機多數是32位,也就是四字節。在每次讀取數據時,一般都是直接讀取32位的數據。有些情況下,字節對齊的數據結構,要比非對齊的數據結構佔用空間少。以下分別就這兩方面舉例闡述。
      1、充分考慮四字節對齊,可以節省存儲空間
         typedef struct tagAAA{
               char name[10];
               long sno;
               char sex;
               float score[4];
         }AAA;
         typedef struct tagBBB{
               char name[10];
               char sex;
               long sno;
               float score[4];
        }BBB;
        在VC下,調試,可以很容易看出來,AAA佔的存儲空間爲36,BBB佔的存儲空間爲32。原因很簡單,在四字節對齊的情況下,按四個字節爲單位分配存儲空間,如果不足,會自動補充,本次分配不足以存放下面的變量時,會重新分配空間。
        AAA:
              |name[0]|name[1]|name[2]|name[3]|
              ------------------------------------
              |name[4]|name[5]|name[6]|name[7]|
              ------------------------------------
              |name[8]|name[9]|             |                   |
                        ----------由於剩下的兩個字節不足以存放sno(long佔四個字節),所以重新分配
              ------------------------------------
              |                        sno                                       |
                          ----------long變量佔四個字節,32bits
              ------------------------------------
              |sex        |    自動填充                                |
                          ----------剩餘三個字節的空間,不足以重放一個float變量,因此重新分配
              ------------------------------------
              |                  score[0]                                     |
              ------------------------------------
              |                  ..........                                        |
              ------------------------------------
              |                  score[3]                                     |
              ------------------------------------
               由此可以輕易的計算出,AAA佔36個字節,同理,很容易計算出BBB佔32個字節空間。
           
         2、字節對其的情況下,可以更高效的訪問
               假設一個結構體的數據如下存儲:
              -----------------------------------------------------
              |        12        |       34       |        56        |        78         |   -----------(A)
              -----------------------------------------------------  
              -----------------------------------------------------
              |        XX        |       YY       |        12       |         34        |   -----------(B)
              -----------------------------------------------------  
              |        56        |        78       |       XX       |         YY        |
              在A情況下,一次性讀取數據成功,但是,在B情況下,需要讀取數據兩次,由此,可看出效率的差異。
          一般情況下,字節對齊遵從系統字節數與要求的對齊字節數相比,最小原則,即:假設要求按八字節對齊,但是系統爲32位系統,則按照4字節對齊。在四字節對齊時,局部會按照2字節對齊,如:
             struct tagAAA
             {
                     char a;
                     short b;
                     char c;
             }AAA;
結構體佔據的空間爲8字節而不是4字節,原因就是:
                    -----------------------------------
                    |    a     |          |             b              |
                    -----------------------------------
                    |   c      |                                      |
而不是:
                    ------------------------------------
                    |    a     |          b          |        c         |
                    ------------------------------------
其原因就是局部會以2字節對齊
 
***********************************************
 
       衆所周知,C語言程序設計中,內存的分配和管理完全交由程序員來控制,因此,內存管理是每個C程序員必須熟練掌握的一般而言,分配給進程的內存有四個概念上不同的區域,分別爲:代碼段、數據段、堆和棧,其中數據段又可以細分爲初始化爲非零的數據和初始化爲零的數據。如下圖所示:
           
            -------------------
            |       程序棧          |----------高地址--〉低地址向下增長
            -------------------
            |          堆                |----------向上增長
            -------------------
            |          BSS              |----------數據段
            | 全局和靜態變量 |
            -----------------------------低地址
            |     可執行代碼     |----------代碼段
            -------------------
       可執行指令放在代碼段中,任何時刻,內存中只有一份相同程序的指令拷貝,多個實例共享這些代碼。初始化爲非零的靜態數據和全局數據存放在數據段中,運行相同程序的每個進程,有自己的數據段。
        初始化爲零(即未初始化的變量,系統自動填充爲0;或者初始化爲0)的全局數據和靜態分配數據存放在進程的BSS區域中,每個運行的進程都有自己的BSS,程序運行的時候,將數據放到數據段中,由此可知,只有初始化爲非零的變量才佔用空間,所以對於類似static int ss[1024];這樣的數組自動用0來填充,它佔的空間很小。
     【BSS“Block Started by Symbol”的縮寫,意爲“以符號開始的塊”。BSS是Unix鏈接器產生的未初始化數據段。其他的段分別是包含程序代碼的“text”段和包含已初始化數據的“data”段。BSS段的變量只有名稱和大小卻沒有值。此名後來被許多文件格式使用,包括PE。“以符號開始的塊”指的是編譯器處理未初始化數據的地方。BSS節不包含任何數據,只是簡單的維護開始和結束的地址,以便內存區能在運行時被有效地清零。BSS節在應用程序的二進制映象文件中並不存在,例如:
  unsigned char var; // 分配到.bss節的8位未初始化變量
  unsigned char var2 = 25; // 分配到.data節的8位已初始化變量
 
      堆,動態內存來自於堆,即:通過malloc得到的空間,通常情況下,堆是向上增長的,即:後面分配的地址比前面的地址在數值上大一些。【注意:這裏的堆並不是數據結構中的堆,它的分配方式類似於鏈表】
 
      棧,分配局部變量的地方,函數參數、函數的返回值和返回地址也放在棧空間中,需要特別注意的是,當函數返回後,存儲在棧空間中的函數變量“自動消失”,空間被其他函數使用。棧空間是向下增長的。
 
      在C語言中,一般通過malloc/calloc函數分配空間,通過free()函數釋放空間,使用realloc()改變已分配空間的大小。
      分配內存的步驟:
      1.申明一個指定類型的指針
      2.計算要分配空間的大小,一般使用函數sizeof()來實現
      3.調用函數malloc()完成空間的申請,將函數的返回值賦給指針變量,
      4.檢查返回值是否不爲NULL,保證空間分配成功
      5.分配好的空間是沒有經過初始化的,其中可能包含一些垃圾信息,因此調用函數memset()將其用0來填充是個好的習慣
      釋放內存步驟:
      1.調用函數free()釋放掉空間
       注意:
        1.不可以使用free()之後的空間
        2.free()後,最好將指針置爲NULL,因爲如果不做這步處理,原來的指針依舊指向剛纔釋放的空間,可以繼續操作
         3.避免重複釋放空間
 
       在Unix系統上,提供了函數alloca()函數,可以實現在棧空間上分配指定大小的空間,這樣的好處是,函數結束後,空間自動釋放,不必顯式地調用函數free(),但是該函數有很多弊端,比如不可移植等,因此不建議使用。
       有必要提一下malloc、calloc、realloc函數的底層實現,在Linux系統中,提供了brk()和sbrk()函數,上面幾個函數就是在這兩個函數的基礎上實現的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章