主頁 > 軟體設計 > 一篇搞定結構體

一篇搞定結構體

2021-09-15 10:20:11 軟體設計

目錄

一、是什么

二、三種結構體宣告

①普通的宣告

②自參考

③特殊的宣告——匿名的結構體

三、三種初始化方法

四、三種解參考方法

五、結構體記憶體對齊(重點)

六、記憶體對齊的意義

七、修改默認對齊值

八、獲取偏移值

九、結構體傳參


一、是什么

結構體是一種自定義型別,用來存放多種型別的資料,使我們得以輕松描述諸如人、書本這些較為復雜的物件,


二、三種結構體宣告

①普通的宣告

struct book
{
	int date;
	float price;
	char name[10];
	char*ps;
	struct content c;
}book1;
typedef struct book
{
	int date;
	float price;
	char name[10];
	char*ps;
	struct content c;
}book;

1.struct+后面的tab(標簽)組成我們創建的變數名,里面可以放入任何型別的變數,甚至是指標和另一個結構體,圖一中book1是我們在定義時順手定義的一個型別為struct book的變數

2.圖二我們將struct book這個型別名重命名為book,這里并沒有定義一個變數,注意區分,


②自參考

struct book
{
	int date;
	struct book b;
}book1;

但注意結構體不能“呼叫”自己 ,如上面的例子,我們可以類比遞回函式的形式,圖中無疑是一個“死回圈”,根本無法計算他的大小,當然編譯器的語法也不通過,要想實作結構體的自參考,我們可以傳入一個指標,實作鏈表一個指向下一個的效果,如下圖:

struct Node
{
	int data;
	struct Node*pn;
};

③特殊的宣告——匿名的結構體

有時候在使用結構體的時候我們直接省去了tag,如下圖,在一定程度上簡化了我們的代碼

struct content
{
	char name[10];
	char id[20];
	int age;
	struct
	{
		int arr[10];
	};

}s1,s2,s3;

雖然匿名結構體沒有名字,但編譯器也是堅決把他們看成兩個不同的型別,如下圖,雖然*px指向的型別看似和x一樣,但編譯器堅決認為兩者是不同的,所以px=&x是錯誤的書寫,

struct
{
	int price;
}x;

struct
{
	int price;
}y,*px;

三、三種初始化方法

//宣告結構體
struct content
{
	int page;
	char* character;
};

typedef struct book
{
	float price;
	char name[20];
	struct content c;
}b;

typedef struct book1
{
	float price;
	char* name;
	struct content c;
}b1;

int main()
{
	//方法一:定義時賦值
	b book1 = { 10.0, "c++", {100,"bit"} };
    //方法二:先定義后賦值
	b1 book2 = {0};
	book2.price = 10.0;
	book2.name = "c++";
	book2.c.page = 100;
	book2.c.character = "bit";
	//定義時亂序賦值
	b1 book3 = {
		.name = "c++",
		.c.page = 100,
		.c.character = "bit",
		.price = 10.0
	};
}

值得注意的是我們不能用陣列來接收例如“c++”,因為常量字串實際傳入的是首元素的地址,所以我們用指標接收,


四、三種解參考方法

struct book
{
	int price;
	char name[10];
};

int main()
{
	struct book b = { 10, "hello" };
	struct book*ps = &b;
	//方法一:->
	printf("%d\n",ps->price);
	//方法二:.
	printf("%d\n",b.price);
	//方法三:本質等同方法二
	printf("%d\n",(*ps).price);
	return 0;
}

五、結構體記憶體對齊(重點)

1.第一個成員在結構體變數偏移量為0的地址處,
2. 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處
對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值,
VS中默認的值為8
Linux中的默認值為4

gcc下沒有默認值
3. 結構體總大小最大對齊數(每個成員變數都有一個對齊數)的整數倍
4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的 整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍,

以下面的例子分析規則一二三,(結果分別為8,12)

struct s1
{
	char a;
	char b;
	int c;

};
struct s2
{
	char a;
	int b;
	char c;

};
int main()
{
	struct s1 s1 = { 0 };
	struct s2 s2 = { 0 };
	printf("%d\n",sizeof(s1));
	printf("%d",sizeof(s2));
}

先來分析s2吧

①a作為第一個成員,創建在結構體偏移量為0的地址,即結構體的起始地址,占用記憶體一位元組,

②b作為第二個成員要考慮偏移量的問題,b的型別int 的大小4<8,所以對齊數為4,對齊在最后未被利用的空間里找一塊地址,其偏移量為對齊數的整數倍

③c作為第三個成員,c的型別為1,偏移量8當然是1的倍數啦,所以c隨即在b的后方開辟空間,但由于規則三,現在的總大小為9,并不是最大對齊數(每個成員的對齊數之間)4的倍數,所以又在后面浪費了3個位元組使得總位元組為12,

同樣的道理分析s1,我們發現是s1的大小是8位元組,同樣的內容卻占用了不同大的空間?我們由此得出以下結論:

為了減小整體所占空間,應該使占用空間小的成員集中在一起

再分析一個內嵌結構體的例子吧!

struct s1
{
	char a;
	char b;
	int  c;
	
};

struct s2
{
	char a;
	char b;
	struct s1 c;
};

int main()
{
	struct s1 s1 = { 0 };
	struct s2 s2 = { 0 };
	printf("%d\n",sizeof(s1));
	printf("%d",sizeof(s2));
}

相同的分析不再贅述,分析一下內嵌結構體,內嵌結構體的最大對齊數是4,整個結構體的最大對齊數也是4,由此我們畫出以下的記憶體示意圖,

內嵌結構體第一個成員進行記憶體對齊(對齊數為內嵌結構體內對齊數的最大值),之后的照123規則對齊,


六、記憶體對齊的意義

由于官方沒有給出明確的規定,我們來學習以下兩個比較有說服力的理由

1. 平臺原因(移植原因): 不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則拋出硬體例外,

2. 性能原因: 資料結構(尤其是堆疊)應該盡可能地在自然邊界上對齊, 原因在于,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問,

想必原因一通俗易懂,我們來解釋一下原因二,

我們知道我們的電腦為32/64根地址線,換算成位元組就是4/8位元組,以4個位元組進行說明,記憶體每四個位元組四個位元組進行訪問時,若結構體的記憶體對齊為四個位元組,則記憶體可以順暢的一次性訪問,若沒有對齊,為了完整訪問,需要兩次,這就降低了效率

總的來說,就是用空間來換取效率


七、修改默認對齊值

我們可以采用預處理指令pragma來修改,如以下的案例:列印結果先后為5 8,

#pragma pack(1)//將默認對齊數改為1;
struct book1
{
	char c;
	int a;
};
#pragma pack()//將默認對齊數改為默認值(8)
struct book2
{
	char c;
	int a;
};
int main()
{
	struct book1 b1 = { 0 };
	struct book2 b2 = { 0 };
	printf("%d\n",sizeof(b1));
	printf("%d\n", sizeof(b2));
	
}

八、獲取偏移值

我們可以采用宏“offsetof”實作,頭檔案為<stddef.h>

#pragma pack(1)//將默認對齊數改為1;
struct book1
{
	char c;
	int a;
};
#pragma pack()//將默認對齊數改為默認值(8)

int main()
{
	struct book1 b1 = { 0 };
	printf("%d\n",offsetof(struct book1,c));
	printf("%d\n",offsetof(struct book1,a));
}

我們也可以用offsetof來驗證修改默認對齊值的情況,


九、結構體傳參

結構體傳參有傳址操作傳參操作之分,但當我們在選擇時,如果需要修改結構體,必須選擇傳址操作;若不用修改,也應該盡可能選擇傳址操作,

原因一:傳址才可以改變結構體的內容

原因二:傳參需要壓堆疊,會帶來記憶體和效率上的損耗,若結構體過大,會導致程式性能下降

若用傳址的方法,但不想結構體被改變,可以采用const修飾

以下用一個簡單的函式分別演示傳址和傳參操作,

struct book
{
	int page;
	float price;
};

void print1(struct book b1)
{
	printf("%d\n",b1.page);
}

void print2(const struct book*ps)
{
	printf("%d\n", ps->page);
}
int main()
{
	struct book b1 = {100,15.0};
	print1(b1);
	struct book*ps = &b1;
	print2(ps);
	return 0;
}

很厲害嘛,堅持看到這里,希望對你有所幫助!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300284.html

標籤:其他

上一篇:(DFS)深度優先搜索演算法詳解

下一篇:破后而立-linux基礎知識點

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more