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

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

3天內不再提示

解析C語言結構體字節(jié)如何對齊

電子工程師 ? 來源:編程學習總站 ? 作者:寫代碼的牛頓 ? 2021-06-12 17:42 ? 次閱讀

01

默認字節(jié)對齊

C語言結構體字節(jié)對齊是老生常談的問題了,也是高頻面試題,現在我們來深入研究這個問題,徹底弄懂到底是怎么回事,給你一個結構體定義和平臺機器位數就能手動計算出結構體占用字節(jié)數,現在我們不使用宏#pragma pack,采用默認字節(jié)對齊方式。

先拋出結論:

在一個結構體中第一個成員變量放在偏移為0的位置,以后的變量都存儲在該變量占用字節(jié)數整數倍的地址上。

結構體總大小,必須是內部最大成員變量的整數倍,不足的補齊。

好了,現在我們直接寫個小程序驗證并分析是否真是這樣一回事。

struct st{ short a1; short a2; short a3; }; struct st2{ long a1; short a2; };

這里我們定義了兩個很簡單的結構體,short占用2個字節(jié),struct st我們一眼就知道大小了6個字節(jié),但是struct st2呢?筆者電腦是64位,那么long占用8個字節(jié),short占用2個字節(jié)。我們先來按照結論進行分析,在struct st2中成員變量a1在偏移0處存儲且占用8個字節(jié),成員變量a2占用2個字節(jié),由于8是2的倍數,所以a2在偏移8的位置存儲,又因為有結論2。

我們根據結論2可以得出,struct st2必須占用8的倍數大小,所以struct st2總大小是16個字節(jié),不足的后面補齊?,F在我分別打印出struct st1和struct st2占用字節(jié)數大小和struct st2各個成員變量地址,觀察是否和分析的一樣。

int main() { struct st2 st_val2; printf(“sizeof(long) = %d ”, sizeof(long)); printf(“sizeof(struct st) = %d ”, sizeof(struct st)); printf(“sizeof(struct st2) = %d ”, sizeof(struct st2)); printf(“st_val2 addr = %p ”, &st_val2); printf(“st_val2 a1 addr = %p ”, &st_val2.a1); printf(“st_val2 a2 addr = %p ”, &st_val2.a2); return 0; }

編譯運行輸出:

sizeof(long) = 8 sizeof(struct st) = 6 sizeof(struct st2) = 16 st_val2 addr = 0x7ffee107f3b8 st_val2 a1 addr = 0x7ffee107f3b8 st_val2 a2 addr = 0x7ffee107f3c0

現在我們看一下輸出結果,struct st如我們所愿占用6個字節(jié)大小,struct st2也按照我們分析的一樣占用16個字節(jié)。我們在程序中定義了一個struct st2類型變量st_val2,從輸出中可以看出變量st_val2的a1成員變量和st_val2變量地址一樣,成員變量a2在偏移8處存儲(0x c0 = 0xb8 + 8)。一切如我們所愿,看起來好像挺簡單的,我們知道C語言有豐富的數據類型,下面我們再定義一個更復雜的結構體。

struct st3{ int a1; char a2; short a3; long a4; char a5; };

這個結構體包含了大量數據類型成員變量,再復雜的結構體也能按照我們的結論分析到底占用了幾個字節(jié)。

在struct st3中int型成員變量a1占用4個字節(jié),在偏移0處存儲,char型成員變量a2占用2個字節(jié)那么應該放在2的倍數地址處存儲,a1已經占用了4個字節(jié),所以a2應該在偏移4的地址存儲。

short型成員變量a3占用2個字節(jié),也應該放在2的倍數地址處存儲,所以a3在偏移6的地址處存儲,a2后面填充1個字節(jié)。

long型成員變量a4占用8個字節(jié),應該放在8的倍數地址上存儲,前面我們已經知道a3在偏移6的地址處存儲,且占用2個字節(jié)8 = 6 + 2,所以a4應該在偏移8的地址處存儲。

最后一個char型成員變量a5占用一個字節(jié),那么a5在偏移16地址處存儲。

現在我們計算一下struct st3結構體占用空間大小,從a5偏移出計算16 + 1 = 17。在struct st3中最大成員變量占用8個字節(jié),所以結構體總大小應該是8的倍數,最后結構體總大小是17 + 7 = 24,這里的7個字節(jié)在最后補齊。

我們依舊寫一個小程序輸出struct st3類型變量各個成員變量地址和結構體總大小。

int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }

編譯運行輸出:

sizeof(struct st3) = 24 st_val3 addr = 0x7ffeed0c33b0 st_val3.a1 addr = 0x7ffeed0c33b0 st_val3.a2 addr = 0x7ffeed0c33b4 st_val3.a3 addr = 0x7ffeed0c33b6 st_val3.a4 addr = 0x7ffeed0c33b8 st_val3.a5 addr = 0x7ffeed0c33c0

從輸出我們可以看出,和我們分析的完全一樣。

枚舉類型變量和聯合體類型變量都可以作為結構體的成員變量,在分析這些結構體占用大小時,分析方法和我們上面的一模一樣,只需要把內部任何一種數據類型變量當做一個普通變量看待即可,但是結構體類型成員變量有點不一樣,它不適用于結論2,我們舉個例子。

struct st4{ char a1[3]; int a2; long a3; struct st3 a4; };

在struct st4中我們定義了一個struct st3類型成員變量,前面我們已經分析過了struct st3占用24個字節(jié)。成員變量a1占用3個字節(jié),成員變量a2占用4個字節(jié),所以a2存儲在偏移4的地址上,在a1后面填充一個字節(jié)。成員變量a3占用8個字節(jié),則a3存儲在偏移8的地址上。那么結構體總共占用字節(jié)數大小是:8 + 8 + 24 = 40。

最后我們寫一個程序驗證一下是否如此。

int main() { struct st4 st_val4; printf(“sizeof(struct st4) = %d ”, sizeof(struct st4)); printf(“st4 addr = %p ”, &st_val4); printf(“st_val4.a1 addr = %p ”, &st_val4.a1); printf(“st_val4.a2 addr = %p ”, &st_val4.a2); printf(“st_val4.a3 addr = %p ”, &st_val4.a3); printf(“st_val4.a4 addr = %p ”, &st_val4.a4); return 0; }

編譯運行輸出:

sizeof(struct st4) = 40 st4 addr = 0x7ffeec1263a0 st_val4.a1 addr = 0x7ffeec1263a0 st_val4.a2 addr = 0x7ffeec1263a4 st_val4.a3 addr = 0x7ffeec1263a8 st_val4.a4 addr = 0x7ffeec1263b0

和我們分析的一模一樣。

02

#pragma pack宏的作用

我們看一下下面這段代碼。

#pagma pack(1)int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }

這段代碼里我們使用了#pagma pack宏,表示結構體按1字節(jié)對齊。也就是說結構體變量st_val3總大小是內部成員變量占用字節(jié)數總和,沒有字節(jié)填充。

現在編譯運行如下:

sizeof(struct st3) = 16 st_val3 addr = 0x7ffee13a93b8 st_val3.a1 addr = 0x7ffee13a93b8 st_val3.a2 addr = 0x7ffee13a93bc st_val3.a3 addr = 0x7ffee13a93bd st_val3.a4 addr = 0x7ffee13a93bf st_val3.a5 addr = 0x7ffee13a93c7

在struct st3中int型a1占用4字節(jié),char型變量a2占用1個字節(jié),short型變量a3占用2個字節(jié),long型變量a4占用8個字節(jié),char型變量a5占用1個字節(jié),所以總大小是:4 + 1 + 2 + 8 + 1 = 16。如果是#pagma pack(2)呢?相信你可以自己計算了。

編輯:jq

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

    關注

    180

    文章

    7594

    瀏覽量

    135861
  • 代碼
    +關注

    關注

    30

    文章

    4722

    瀏覽量

    68234

原文標題:C語言結構體字節(jié)對齊

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    C語言C++中結構的區(qū)別

    同樣是結構,看看在C語言C++中有什么區(qū)別?
    的頭像 發(fā)表于 10-30 15:11 ?101次閱讀

    你是否真的了解結構占用了多少字節(jié)?

    結構成員所占內存空間大小一般情況下,如果想知道結構成員的內存占用情況需要:1、先用結構在內
    的頭像 發(fā)表于 06-04 08:04 ?362次閱讀
    你是否真的了解<b class='flag-5'>結構</b><b class='flag-5'>體</b>占用了多少<b class='flag-5'>字節(jié)</b>?

    嵌入式中C語言結構基本實現

    C語言中的數組只能允許程序員定義存儲相同類型數據。但是結構C語言編程中允許您存儲不同數據類型的數據。
    的頭像 發(fā)表于 05-11 08:49 ?908次閱讀
    嵌入式中<b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>結構</b><b class='flag-5'>體</b>基本實現

    C語言結構史上最詳細的講解【軟件干貨】

    struct結構數據類型 前言 我們知道,在C語言中有一些基本的數據類型,如?char?int?float?long?double?string(
    的頭像 發(fā)表于 03-28 17:52 ?676次閱讀

    嵌入式系統中C語言結構的基礎實現與應用

    C語言中的數組只能允許程序員定義存儲相同類型數據。但是結構C語言編程中允許您存儲不同數據類型的數據。
    發(fā)表于 03-12 14:29 ?404次閱讀
    嵌入式系統中<b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>結構</b><b class='flag-5'>體</b>的基礎實現與應用

    經典 C 語言編程,結構和聯合體如何共用?

    結構 結構占用的內存大小,首先和編譯器的系統位數有關系,類似于CPU是 64 bits 還是 32 bits 的情形;其次,結構
    的頭像 發(fā)表于 01-11 18:24 ?1230次閱讀
    經典 <b class='flag-5'>C</b> <b class='flag-5'>語言</b>編程,<b class='flag-5'>結構</b><b class='flag-5'>體</b>和聯合體如何共用?

    結構與指針的關系

    C語言中,結構(Struct)是一種用戶自定義的數據類型,它允許您將不同類型的數據項組合在一起,以便形成一個更復雜的數據結構
    的頭像 發(fā)表于 01-11 08:00 ?919次閱讀
    <b class='flag-5'>結構</b><b class='flag-5'>體</b>與指針的關系

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

    的默認對齊方式和規(guī)則 結構在Keil Arm工程中的默認對齊方式是根據編譯器或者編譯器選項來定的。通常情況下,編譯器會按照平臺的特定對齊
    的頭像 發(fā)表于 01-05 14:40 ?3320次閱讀

    C語言結構體能不能相加

    C語言中,結構體能不能相加?
    的頭像 發(fā)表于 12-19 17:04 ?1192次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b>中<b class='flag-5'>結構</b>體能不能相加

    嵌入式C語言結構特點

    過程中,不論是基于寄存器開發(fā)還是基于庫開發(fā),深入理解和掌握嵌入式C語言的函數、指針、結構是學習STM32的關鍵。嵌入式C
    的頭像 發(fā)表于 11-24 16:16 ?619次閱讀
    嵌入式<b class='flag-5'>C</b><b class='flag-5'>語言</b>的<b class='flag-5'>結構</b>特點

    c語言中順序結構的基本流程

    C語言是一種通用的編程語言,注重結構化編程和順序結構。順序結構
    的頭像 發(fā)表于 11-22 10:20 ?913次閱讀

    嵌套的結構 字節(jié)是如何對齊

    嵌套的結構字節(jié)又是如何對齊的呢 先來看下面的代碼 typedef struct stu1 { char ary [ 5 ] ; int a; }stu1; typedef stru
    的頭像 發(fā)表于 11-20 16:01 ?488次閱讀
    嵌套的<b class='flag-5'>結構</b><b class='flag-5'>體</b> <b class='flag-5'>字節(jié)</b>是如何<b class='flag-5'>對齊</b>的

    什么是結構字節(jié)對齊現象

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

    C語言結構(struct)用法詳解

    在實際的開發(fā)過程中,一組數據往往具有不同的數據類型,此時數組是不能夠滿足需求了。因為數組中各元素的類型必須是一致的。為了解決這個需求,C中給出了另一種數據類型-結構,每一個成員可以是任意一種數據類型。
    的頭像 發(fā)表于 11-15 16:11 ?1750次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>結構</b><b class='flag-5'>體</b>(struct)用法詳解

    C語言映射表在串口數據解析中的應用

    C語言映射表在串口數據解析中的應用
    的頭像 發(fā)表于 11-13 09:33 ?440次閱讀