如何設計結構體

2021-12-28 CPP開發者

之前寫過一篇《如何設計一個C++的類》,今天這裡繼續聊聊如何設計結構體,注意本文不介紹在C++中結構體和類具體有什麼區別,本文所說的結構體是指只有數據欄位不帶任何函數的那種結構體。

當創建結構體的實例時,結構體的數據成員會按其聲明的順序連續存儲。然而,這個聲明的順序也是有學問的,順序不同結構體的大小可能有很大差別,數據成員的訪問性能也可能會有很大區別!

這裡涉及一個概念:內存對齊。關於內存對齊我之前寫過一篇文章:《內存對齊》,這裡不深入討論,只是簡單介紹一下。

大多數編譯器會對齊數據成員,會以四捨五入地址方式來優化數據的訪問,如下表所示。

這種內存對齊可能會在成員大小混合的結構體中產生未使用字節的空洞。
例如:

struct S {
    short int a; // 2位元組
    // 6個空洞
    double b; // 8
    int d; // 4
    // 4個空洞
};
S ArrayOfStructures[100];

這裡,在a和b之間有6個未使用的字節,因為b必須從一個能被8整除的地址開始。

最後還有4個未使用的字節空洞。這樣做的原因是,數組中S的下一個實例必須從一個能被8整除的地址開始,以便將其b成員以8對齊。

然而,如果改變一下結構體中數據成員聲明的順序,通過將最小的成員放在最後,未使用的字節數可以減少到2:

struct S {
    double b; // 8
    int d; // 4
    short int a; // 2
    // 2個空洞
};
S ArrayOfStructures[100];

這種重新排序使結構體變小了8個字節,那整個數組則變小了800個字節。

在此特性上,類和結構體相同。通過重新排序數據成員,結構體對象和類對象通常可以變得更小。如果類至少有一個虛成員函數,則在第一個數據成員之前或最後一個成員之後會有一個指向虛函數表的指針。該指針在32位系統中為4位元組,在64位系統中為8位元組。

如果不確定結構體或它的每個成員有多大,可以使用sizeof操作符進行一些測試。sizeof操作符返回的值包括對象末尾的任何未使用的字節(內存對齊後的字節數)。

還有一個知識點:

如果數據成員相對於結構體或類開頭的偏移量小於128,則訪問數據成員的代碼會更加緊湊,因為該偏移量可以使用8位有符號的數字來表示。如果相對於結構體或類的開頭的偏移量是128位元組或更多,那麼偏移量必須表示為一個32位數字(指令集在8位到32位之間沒有偏移量)。例如:

struct S {
    int a[100]; // 400
    int b; // 4
    int read() { return b; }
};

b成員的偏移量是400。任何通過指針或成員函數訪問b欄位的代碼都需要將偏移量編碼為32位數字。如果交換a和b,則兩者都可以通過編碼為8位有符號數字的偏移量來訪問,或者根本不需要偏移量。

這會使代碼更緊湊,方便更有效地使用代碼緩存。因此,建議在結構或類聲明中,大數組和其他大對象排在最後,最常用的數據成員排在前面。如果不能在前128個字節內包含所有數據成員,則將最常用的成員放在前128個字節中。
通過上面兩個小知識點可以使得將結構體設計的更小,訪問數據成員的速度更快,但是這有時往往會犧牲一些可讀性,比如這種結構體:

struct S {
    int deskA;
    double deskB;
    bool deskC;
    int chairA;
    double chairB;
    bool chairC;
};

可能這樣修改後結構體會更小:

struct S {
    int deskA;
    int chairA;
    double deskB;
    double chairB;
    bool deskC;
    bool chairC;
};

但是我們一般情況下貌似希望同類的欄位放在一起,這樣代碼可讀性更高一些,易於讀懂代碼。至於這種結構體具體需不需要重新排序,那就需要大家自己權衡啦。

小總結:

128是個檻,常用的數據成員可考慮放在前128位元組中,不常用的或大的數據成員可考慮放在後面;

打完收工。

- EOF -

關注『CPP開發者』

看精選C++技術文章 . 加C++開發者專屬圈子

點讚和在看就是最大的支持❤️

相關焦點

  • 結構體
    結構體概述在前面章節中介紹了 C 語言中的多種數據類型,例如:整型、字符型、浮點型、數組、指針……等等。但是在實際開發中,只有這些數據類型是不夠的,難以勝任複雜的程序設計。例如:在員工信息管理系統中,員工的信息就是一類複雜的數據。每條記錄中都包括員工的姓名、性別、年齡、工號、工資等信息。姓名為字符數組、性別為字符、年齡為整型、工號為整型、工資為整型。
  • C語言 | 結構體變量
    「要成為絕世高手,並非一朝一夕,除非是天生武學奇才,但是這種人…萬中無一
  • 快速上手系列-C語言之結構體(二)結構體數組與結構體指針
    一個結構體變量中可以存放一組數據;如一個學生的學號、姓名、成績等數據.如果有10個學生的數據需要參加運算顯然應該用數組,這就是結構體數組(如果定義10個結構體變量太麻煩了)結構體數組與以前介紹過的數值型數組不同之處:每個數組元素都是一個結構體類型的數據,它們都分別包括各個成員(分量)項以下以直接定義結構體數組為例:我們定義了一個結構體類型是struct student,它有三個成員分別是
  • C語言之結構體(struct)
    我們先看看使用 struct 如何表示一個像素(用文件夾裝在一起):Pixel 中文表示像素,這樣就通過這個結構體將三個數據結合在一起了,並且這個新組合的數據類型就叫 Pixel,和 int、char 等類似。
  • 乾貨|手把手教你寫單片機的結構體
    摘要:聽說還有好多學單片機的小夥伴不會用結構體?指針和結構體是學單片機必須要掌握的,如果你C語言掌握的不牢,單片機根本學不到精髓,只能完成一些低級的項目。看得懂結構體並且能夠靈活運用結構體才能說你入門了單片機。本篇將以最通俗的方式結合STM32單片來講講結構體的運用。
  • Golang 入門 : 結構體(struct)
    結構體由一系列命名的元素組成,這些元素又被稱為欄位,每個欄位都有一個名稱和一個類型。結構體的目的就是把數據聚集在一起,以便能夠更加便捷地操作這些數據。結構體的概念在 C 語言裡很常見,被稱為 struct。Golang 中的結構體也是 struct。Go 語言中沒有類的概念,因此在 Go 中結構體有著更為重要的地位。
  • C語言結構體(struct)最全的講解
    在結構體中的變量,可以是相同、部分相同,或完全不同的數據類型。在C語言中,結構體不能包含函數。在面向對象的程序設計中,對象具有狀態(屬性)和行為,狀態保存在成員變量中,行為通過成員方法(函數)來實現。C語言中的結構體只能描述一個對象的狀態,不能描述一個對象的行為。
  • 為什麼RECT結構體被設計為開區間
    為什麼RECT結構體被設計為開區間端點?主要是為了編程的時候能方便一些。舉個例子,一個矩形的寬度可以簡單地表示為 (right – left),高度表示為 (bottom – top)。如果它被設計為閉區間端點,則計算寬度和高度時就必須考慮+1的問題。
  • struct2json V1.0 發布,C 結構體與 JSON 互轉庫
    struct2json 是一個開源的C結構體與 JSON 快速互轉庫,它可以快速實現 結構體對象 與 JSON 對象 之間序列化及反序列化要求
  • 51單片機之C語言-4.3結構體
    當然可以,這個就叫做結構體。現在按照數組的學習方法來研究結構體,兩個問題,如何定義;如何訪問結構體成員。(1) 如何定義一個結構體本文引用地址:http://www.eepw.com.cn/article/201611/319552.htm定義一個結構的一般形式為:struct結構名{成員表列};成員表列由若干個成員組成,每個成員都是該結構的一個組成部分。
  • C語言結構體(struct)最全的講解(萬字乾貨)
    在結構體中的變量,可以是相同、部分相同,或完全不同的數據類型。在C語言中,結構體不能包含函數。在面向對象的程序設計中,對象具有狀態(屬性)和行為,狀態保存在成員變量中,行為通過成員方法(函數)來實現。C語言中的結構體只能描述一個對象的狀態,不能描述一個對象的行為。
  • C語言結構體變量
    為了解決這樣的問題,就要用到結構體這種構造類型,我們可以將每個學生的各項信息以不同類型的數據存放到一個結構體中,如用字符型表示姓名,用整型或字符型表示學號、用整型或實型表示成績。結構體變量的定義結構體就是將不同類型的數據組合成一個有機的整體,以便於引用。
  • C語言之結構體
    int (*POINTER)(int ,char*);     3、在最前邊加typedef     typedef int (*POINTER)(int ,char*);     最後一步是咱們想要的,以後就可以使用POINTER 定義變量了     定義的變量,是函數指針變量,和p是一個類型的變量     POINTER q;//定義了一個函數指針變量
  • 指向結構體的指針
    創建結構體指針是極常見的。下面是一個例子:typedef struct{ char name[21]; char city[21]; char state[3];} Rec;typedef Rec *RecPointer;RecPointer r;r=(RecPointer)malloc(sizeof(Rec));r是一個指向結構體的指針。
  • C語言結構體(Struct)
    在C語言中,可以使用結構體(Struct)來存放一組不同類型的數據。結構體的定義形式為:struct 結構體名{    結構體所包含的變量或數組};結構體是一種集合,它裡面包含了多個變量或數組,它們的類型可以相同,也可以不同,每個這樣的變量或數組都稱為結構體的成員(Member)。
  • C語言系列(九):結構體
    類型n 成員名n;};其中,struct是定義結構體類型的關鍵字;結構體類型名必須是合法的C標識符,與其前面的struct一起共同構成結構體類型名;花括號內的內容是結構體類型所包括的結構體成員,也稱為結構體分量。
  • 結構體變量建模之終極解決方案
    結構體類型是一種組合數據類型,不同於數組,結構體的元素是可以有不同數據類型的,上面的例子中a、b、c的數據類型各不相同。我們可以像使用基本數據類型一樣使用結構體類型去定義變量,使用結構體類型定義的變量是結構體變量,比如,            mystruct_T mystruct_var; 那麼mystruct_var就是一個結構體變量,可以通過如下結構體變量右側加點來訪問結構體變量中的元素,比如            mystruct_var.a = 30;
  • 9.3 結構體指針
    (1)通過指向結構體變量的指針輸出結構體變量中的成員信息#include<stdio.h>//指向結構體變量的指針
  • C語言編程 — 結構體與位域
    我們可以使用結構體表示更加複雜的數據類型。注意:我們應該將結構體定義在所有需要用到它的函數的上方,枚舉類型和基本數據類型的使用方法沒有任何區別。可見,結構體就像一個 「模板」,定義出來的變量都具有相同的性質,可以使用結構體來實現 C++ 中的類和實例的繼承機制。結構體的定義具有多種方式,比較靈活。
  • 面試問:C#結構體和類的區別,結構體的使用場景
    昨天發布了一篇關於結構體的文章,但是對結構體的用法還比較模糊,這篇文章將介紹結構體和類的區別,以及結構體的使用