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

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

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

3天內不再提示

結構體對齊為什么那么重要?

嵌入式軟件實戰派 ? 來源:嵌入式軟件實戰派 ? 2023-04-03 10:13 ? 次閱讀
C語言結構體對齊問題,是面試必備問題。我參與招聘技術面試的時候,也喜歡問這個技術點。這不是在面試時要裝B,也不是要故意難為一下面試者,而是這個知識點比較基礎,但很重要。網上搜出來的嵌入式或C語言筆試題,很多都有這種題目,連《程序員面試寶典》也有講解這種題目。

91fbde72-d1c4-11ed-bfe3-dac502259ad0.png

結構體對齊知識點考察,儼然成為編程技術崗面試筆試的一種標配。我以前找工作被問這種題的時候就經常想,結構體對齊這個東西平常很少用,考這東西干嘛?為什么結構體對齊那么重要。看看這個例子
    typedef struct 
    {
        int e_int;
        char e_char1;
        char e_char2;
    }S2;


    typedef struct 
    {
        char e_char1;
        int e_int;
        char e_char2;
    }S3;
S2s2;
    S3 s3;

你覺得這倆結構體所占內存是一樣大嗎?其實不是!

好像也沒什么啊,一不一樣大對于C語言程序員有什么所謂!

也許你還還感覺不到,上段代碼:

    S2 s2[1024] = {0};
    S3 s3[1024] = {0};

對于32位系統,s2的大小為8K,而s3的大小為12K,一放大,就有很明顯的區別了。

再舉個例子:

unsignedcharbytes[10]={0};
int* p = (int*)&bytes[3];
*p = 0x345678;
你覺得執行上面的代碼會發生什么情況?Warining?只是Warning么?! 以前我也沒覺得懂得這個結構體對齊或者內存對齊有多重要,直到已經從事了嵌入式開發經驗不斷積累,才慢慢體會到,這是一種很基礎的知識,就因為這個東西不常用,而出現相關的問題是非常致命的,排查起來成本非常高。 有個小伙伴,因為一個內存對齊(結構體對齊相關知識點)問題導致的偶發性Exception問題,折騰了一個多星期。 由于項目接近尾聲,出現這種問題,項目經理、老板都操心得不得了。天天不是奶茶水果,就是宵夜,把小伙伴當寶貝來哄,為的就是快速定位這個問題。 然而,他們日以繼夜的排查了一個多星期,依然一臉懵逼。 直到讓我參與進來支援,我通過仿真方式碰巧捕捉到了這種異常情況。問題的根本原因就是強制類型轉換導致的內存對齊問題。篇幅有限,這個故事,以后慢慢細講。 接下來先看看,結構體對齊的知識點。 結構體對齊,說不難吧,我研究了很多次,都沒完全記住;說難吧,理解其原因本質,就易如破竹。結構體對齊,其實其本質就是內存對齊。 什么以最大元素變量為單位,什么最小公倍數等等法則,通通都是讓你死記硬背的,沒兩天就忘了。 為什么要結構體對齊,原因就是內存要對齊,原因是芯片內存的制造限制,是制造成本約束,是內存讀取效率要求。 如果你上學的時候認真學習過微機原理,應該還記得,芯片的地址總線和數據總線這個概念吧。沒學過微機原理也沒關系,8位單片機、16位單片機和32位單片機等等,這些總得聽說過吧。

92185124-d1c4-11ed-bfe3-dac502259ad0.png

這個8位、16位和32位等,指的是單片機一次處理數據的寬度,也就和數據總線相關了。 細心的小伙伴會知道,16位單片機的通用寄存器例如R0的長度是2個字節的,而32位的是4字節的。 也就是說16位單片機,單指令一次訪問數據是2個字節,而32位單片機可以訪問4字節。 為了提高MCU的運行效率,內存設計上,進來適應這個CPU的總線訪問。以32位MCU為例,其內存一般都是每4字節(32位)為一個小單元,有時候也叫1個字(Word)。922797ba-d1c4-11ed-bfe3-dac502259ad0.png 注意:字節,這個概念長度是固定的,就是8bit;而,卻不是固定的,跟CPU或系統位數有關,有時候還會出現字、雙字這些概念,舉例說明下: 32位計算機:1字=32位=4字節,64位計算機:1字=64位=8字節所以,對于C語言的變量的存放和訪問,都會按著這單位來,例如32位系統中,char是一個字節的,就按Byte來,int是4字節的,那么按Word來。為什么要這樣呢? 如果,一塊內存在地址上隨便放的,CPU有可能就會用到多條指令來訪問,這就會降低效率。 對于32位系統,如下圖的A可能需要2條指令訪問,而B只需1條指令。

92393b6e-d1c4-11ed-bfe3-dac502259ad0.png

92463fbc-d1c4-11ed-bfe3-dac502259ad0.png 不僅單片機這樣,我們常用的計算機也是這樣,你看內存條,長這樣的:

925ba398-d1c4-11ed-bfe3-dac502259ad0.png

你以為,通過總線的方式可以隨便訪問一個地址嗎

92a9b83a-d1c4-11ed-bfe3-dac502259ad0.png

但是,為了提高訪問速度,其設計是這樣的:

92b9f54c-d1c4-11ed-bfe3-dac502259ad0.png

92cdd83c-d1c4-11ed-bfe3-dac502259ad0.png

這樣,這個地址就必須是8的倍數。如果你要從不對齊的內存讀取數據,雖然在C語言編程上感覺不到這樣的操作有什么區別,但CPU是分開多次讀出來的。這就是內存對齊了。int8(即char)是以1字節對齊,int16是以2字節對齊,而int32是以4字節對齊的,等等。(以上案例看不懂?推薦去B站看這個視頻:【Golang】這個內存對齊呀!?_嗶哩嗶哩_bilibili,我上面的圖也是參考這個視頻的。)世界上CPU平臺、系統那么多,我們怎么知道哪個類型到底有多長,是以哪種長度對齊的?

不要瞎猜,直接上代碼。每個平臺都不一樣,請讀者自行測試,以下我是基于Windows上MinGW的GCC測的。

#defineBASE_TYPE_SIZE(t)printf("%12s:%2dByte%s
",#t,sizeof(t),(sizeof(t))>1?"s":"")
void base_type_size(void)
{
    BASE_TYPE_SIZE(void);
    BASE_TYPE_SIZE(char);
    BASE_TYPE_SIZE(short);
    BASE_TYPE_SIZE(int);
    BASE_TYPE_SIZE(long);
    BASE_TYPE_SIZE(long long);
    BASE_TYPE_SIZE(float);
    BASE_TYPE_SIZE(double);
    BASE_TYPE_SIZE(long double);
    BASE_TYPE_SIZE(void*);
    BASE_TYPE_SIZE(char*);
    BASE_TYPE_SIZE(int*);
    
    typedef struct 
    {
    }StructNull;
    BASE_TYPE_SIZE(StructNull);
    BASE_TYPE_SIZE(StructNull*);
}

結果是:

        void :  1 Byte
        char :  1 Byte
       short :  2 Bytes
         int :  4 Bytes
        long :  4 Bytes
   long long :  8 Bytes
       float :  4 Bytes
      double :  8 Bytes
 long double : 12 Bytes
       void* :  4 Bytes
       char* :  4 Bytes
        int* :  4 Bytes
  StructNull :  0 Byte
 StructNull* :  4 Bytes

這些內容不用記住,不同平臺是不一樣的,使用之前,一定要親自測試驗證下。

這里先解釋下“模數”的概念:

每個特定平臺上的編譯器都有自己的默認“對齊系數”(也叫對齊模數)。

接著看網上流傳一個表:

平臺

長度/模數

char

short

int

long

float

double

long long

long double

Win-32

長度

1

2

4

4

4

8

8

8

模數

1

2

4

4

4

8

8

8

Linux-32

長度

1

2

4

4

4

8

8

12

模數

1

2

4

4

4

4

4

4

Linux-64

長度

1

2

4

8

4

8

8

16

模數

1

2

4

8

4

8

8

16

本文的的例子我用的是MinGW32的GCC來測試,你猜符合上表的哪一項?

別急,再看一個例子:

    typedef struct 
    {
        int e_int;
        double e_double;
    }S11;
    S11 s11;
STRUCT_E_ADDR_OFFSET(s11,e_int);
    STRUCT_E_ADDR_OFFSET(s11, e_double);

結果是:

  s11 size = 16        s11.e_int addr: 0028FF18, offset:  0
  s11 size = 16     s11.e_double addr: 0028FF20, offset:  8

很明顯,上表沒有一項完全對應得上的。簡單匯總以下我測試的結果:

長度/模數

char

short

int

long

float

double

long long

long double

長度

1

2

4

4

4

8

8

12

模數

1

2

4

4

4

8

8

8

所以,再強調一下:因為環境的差異,在你參考使用之前,請自行測試一下。

其實,這個模數是可以改變的,可以用預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數,其中的n就是你要指定的“對齊系數”。

例如

#pragma pack(1)
typedef struct 
{
    char e_char;
    long double e_ld;
}S14;
#pragma pack()
想知道結構圖元素內存如何對齊,其實非常簡單。其實,你只需知道當前你使用的這個系統的基本類型的sizeof是多少,然后根據這個大小做對齊排布。例如,本文一開始的例子
    typedef struct 
    {
        int e_int;
        char e_char1;
        char e_char2;
    }S2;


    typedef struct 
    {
        char e_char1;
        int e_int;
        char e_char2;
    }S3;
S2s2;
    S3 s3;

32位系統中,它們內存是這么對齊的:

92ec6414-d1c4-11ed-bfe3-dac502259ad0.png

簡單解釋下:

S2中的元素e_int是按4字節對齊的,其地址位4整數倍,而e_char1和e_char2就按1字節對齊,緊跟其后面就可以了;

而S3中的元素e_char1是按1字節對齊的,放在最前面,而e_int是按4字節對齊的,其地址位4整數倍,所以,只能找到個+4的位置,緊接著e_char2就按1字節對齊,跟其后面就可以了。

那么sizeof(s2)和sizeof(s3)各是多少怎么算?

也很簡單,例如這個32位系統,為了提高執行效率,編譯器會讓數據訪問以4字節為單位的,所以S2里有2個字節留空,即sizeof(s2)=8,而sizeof(s3)=12。

是不是很簡單呢!

接著,來個復雜一點的:

    typedef struct 
    {
        char e_char1;
        short e_short;
        char e_char2;
        int e_int;
        char e_char3;
    }S4;
S4s4;

其內存分布如下:

92ffb9ce-d1c4-11ed-bfe3-dac502259ad0.png按上面的方法,也不難理解。e_int是不能從+5位置開始的,因為+5不是int的對齊位置,用int去訪問+5位置是效率很低或者有問題的,所以它只能從+8位置開始。再復雜一點的呢?來看看union和struct結合的例子:
    typedef struct
    {
        int e_int1; 
        union
        {
            char ue_chars[9]; 
            int ue_int;
        }u;
        double e_double; 
        int e_int2; 
    }SU2;
SU2su2;
得到:

930f279c-d1c4-11ed-bfe3-dac502259ad0.png

為什么這樣呢?你這樣想,要時刻想著CPU訪問數據的效率,如果union里的元素類型不一樣,那就以最大長度的那個類型對齊了。另外,還有結構體套著結構體的情況了:
typedef struct 
    {
        int e_int;
        char e_char;
}S1;
typedef struct 
    {
        S1 e_s;
        char e_char;
    }SS1;


    typedef struct 
    {
        short e_short;
        char e_char;
    }S6;


    typedef struct 
    {
        S6 e_s;
        char e_char;
}SS2;

得出結果:

93267cb2-d1c4-11ed-bfe3-dac502259ad0.png

得出結論:結構體內的結構體,結構體內的元素并不會和結構體外的元素合并占一個對齊單元。

只要技術上面的對齊方法,這些都不難理解。如果你非要一些規則的話,我總結成這樣:

首先,不推薦記憶這些條條框框的文字,以下內容僅供參考:

  1. 結構體的內存大小,并非其內部元素大小之和;
  2. 結構體變量的起始地址,可以被最大元素基本類型大小或者模數整除;
  3. 結構體的內存對齊,按照其內部最大元素基本類型或者模數大小對齊;
  4. 模數在不同平臺值不一樣,也可通過#pragma pack(n)方式去改變;
  5. 如果空間地址允許,結構體內部元素會拼湊一起放在同一個對齊空間;
  6. 結構體內有結構體變量元素,其結構體并非展開后再對齊;
  7. union和bitfield變量也遵循結構體內存對齊原則。
其實,這些都沒必要去記,多思考多理解就OK了。唯一需要記得是某系統平臺下的基本類型的sizeof大小,然后按照對齊原則來就可以了,就是時刻想著CPU要提升數據訪問效率的。 更多的案例,很早寫在《圖文并茂,一文講透C語言結構體內存對齊》這個文章里面了,感興趣的小伙伴可以研究下。

里面涉及到很多測試源碼,如果想要獲取的話,可以關注公眾號,回復"struct"即可獲得下載鏈接。

?審核編輯 :李倩



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

    關注

    180

    文章

    7615

    瀏覽量

    137855
  • 代碼
    +關注

    關注

    30

    文章

    4837

    瀏覽量

    69130
  • 結構體
    +關注

    關注

    1

    文章

    130

    瀏覽量

    10873

原文標題:結構體對齊為什么那么重要?

文章出處:【微信號:embedded_sw,微信公眾號:嵌入式軟件實戰派】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    C語言結構對齊介紹

    大家好,我是嵌入式老林,從事嵌入式軟件開發多年,今天分享的內容是C語言結構對齊介紹,希望能對你有所幫助
    發表于 07-11 11:50 ?2595次閱讀
    C語言<b class='flag-5'>結構</b><b class='flag-5'>體</b><b class='flag-5'>對齊</b>介紹

    C語言-結構對齊詳解

    `C語言-結構對齊詳解朱有鵬1、結構為何要對齊訪問訪問
    發表于 07-12 16:41

    CCS3.3 結構成員對齊

    嗎?如果不支持,那么怎么樣來改變其結構對齊方式?請朋友們不吝賜教。 另外 用#pragma pack()會提示#pragma 不被識別的錯誤。這是怎么回事?
    發表于 06-21 10:16

    請問在ccs4.2 中怎么設置結構的字節對齊

    請問在ccs4.2 中怎么設置結構的字節對齊,用于網絡數據發送的。是:]__attribute__((packed))還是:#pragma pack(1)編譯后,兩種都不行,是什么原因?有沒有其他方法,把
    發表于 08-02 07:47

    請問z-stack結構默認對齊方式是一字節嗎?

    z-stack的結構默認對齊方式是一字節嗎?在z-stack中可以將一般指針強制轉換為結構指針嗎?
    發表于 08-18 07:38

    關于labview傳入參數到DLL結構

    labview給DLL中結構傳入參數,要保證字節對齊下面是注意事項,很關鍵:labview中層次結構數據類型(例如,簇)中的數組和字符串始終包括大小信息。所以簇內存中包含字符串的大小
    發表于 11-08 20:30

    結構變量的定義與使用變量訪問結構成員

    知識點回顧關于找最大公共子串的兩種解題方法結構的定義(3種)結構變量的定義與使用變量訪問結構
    發表于 12-17 07:10

    測試結構成員內存對齊的方式方法

    //測試環境:keil for ARM//測試目的:通過keil仿真,介紹結構成員對齊方式 #pragma pack ()//定義一個聯合體類型 struct stru {int a;long b
    發表于 12-21 07:37

    為什么ST庫函數結構沒加對齊地址是連續的?

    為什么ST庫函數結構沒加對齊,地址是連續的
    發表于 10-15 08:11

    固態硬盤4K對齊操作對齊的到底是什么?為什么它如此重要

    在購買和使用固態硬盤的過程中,大家一定都聽說過“注意4K對齊”這個提醒。那么4K對齊到底對齊了什么?為什么它如此重要
    的頭像 發表于 06-04 09:03 ?9200次閱讀

    解析C語言結構字節如何對齊

    01 默認字節對齊 C語言結構字節對齊是老生常談的問題了,也是高頻面試題,現在我們來深入研究這個問題,徹底弄懂到底是怎么回事,給你一個結構
    的頭像 發表于 06-12 17:42 ?3156次閱讀

    結構對齊理解上有點偏差

    總結一下: 結構對齊不再是簡單的字節個數的拼湊,而是要與內存地址進行掛鉤~一般我們也可以理解為內存地址分配是多少字節的倍數,就是多少直接對齊~
    的頭像 發表于 08-10 18:08 ?1244次閱讀
    對<b class='flag-5'>結構</b><b class='flag-5'>體</b>的<b class='flag-5'>對齊</b>理解上有點偏差

    為什么要結構對齊?為什么結構對齊那么重要

    C語言結構對齊問題,是面試必備問題。我參與招聘技術面試的時候,也喜歡問這個技術點。
    的頭像 發表于 05-26 14:10 ?1360次閱讀
    為什么要<b class='flag-5'>結構</b><b class='flag-5'>體</b><b class='flag-5'>對齊</b>?為什么<b class='flag-5'>結構</b><b class='flag-5'>體</b><b class='flag-5'>對齊</b><b class='flag-5'>那么</b><b class='flag-5'>重要</b>?

    什么是結構的字節對齊現象

    什么是結構的字節對齊現象 程序員,咱都用代碼說話,先上 code: (說明:以下代碼均在 ARM 平臺上,使用 Keil 進行編譯測試) # define offset_of (TYPE
    的頭像 發表于 11-20 15:55 ?684次閱讀
    什么是<b class='flag-5'>結構</b><b class='flag-5'>體</b>的字節<b class='flag-5'>對齊</b>現象

    keil arm工程中結構1字節對齊如何實現

    在Keil Arm工程中,結構對齊方式可以通過使用特定的編譯器指令或者關鍵字來實現。結構對齊
    的頭像 發表于 01-05 14:40 ?4079次閱讀
    主站蜘蛛池模板: 日本精品视频四虎在线观看 | 性生大片一级毛片免费观看 | 婷婷丁香社区 | 中文字幕在线一区二区在线 | 女的扒开尿口让男人桶 | 精品乱人伦一区二区三区 | 精品国产免费人成高清 | 亚洲国产日韩精品怡红院 | 国产成人在线网址 | a级毛片毛片免费很很综合 a级男女性高爱潮高清试 | 亚洲精品久久久久午夜三 | 天天做天天爱天天爽天天综合 | 2020夜夜操| 日本aaaaa毛片动漫 | 日韩精品视频免费在线观看 | 黄色网在线播放 | 日本a级在线 | 午夜在线观看免费高清在线播放 | 男人的午夜天堂 | 老外一级黄色片 | 欧美人与性另类 | 年下攻高h好涨 | 夜夜春宵翁熄性放纵30 | 久久久噜噜噜久久久 | 国产情侣露脸 | 91在线操 | 日韩一级片在线免费观看 | 绝色村妇的泛滥春情 | 国产精品自在线天天看片 | 伊人婷婷涩六月丁香七月 | 五月天激激婷婷大综合丁香 | 欧美一区二区三区男人的天堂 | 美女视频黄的免费视频网页 | 亚洲第一区视频 | 色男人在线 | 午夜影视免费完整高清在线观看网站 | 日本三级香港三级三级人!妇久 | 亚洲一区二区免费视频 | 成人激情在线 | 日本一视频一区视频二区 | 色婷婷六月丁香七月婷婷 |