主頁 > 軟體設計 > 自定義型別~結構體~位段~列舉~聯合~超詳解~一遍就會

自定義型別~結構體~位段~列舉~聯合~超詳解~一遍就會

2021-09-21 11:35:44 軟體設計

目錄

結構體

1.結構體型別宣告

1.1 結構的基礎知識

1.2 結構的宣告

1.3 特殊的宣告

1.4 結構的自參考

1.5 結構體變數的定義和初始化

1.6 結構體記憶體對齊

1.7 修改默認對齊數

1.8 結構體傳參

2. 位段

2.1 什么是位段

2.2 位段的記憶體分配

2.3 位段的跨平臺問題

2.4 位段的應用

3. 列舉

3.1 列舉型別的定義

3.2 列舉的優點

3.3 列舉的使用

4. 聯合(共用體)

4.1 聯合型別的定義

4.2 聯合的特點

4.3 聯合大小的計算

結構體

1.結構體型別宣告

1.1 結構的基礎知識


結構是一些值的集合,這些值稱為成員變數,結構的每個成員可以是不同型別的變數

1.2 結構的宣告

struct tag
{
member-list;
}variable-list;


例如描述一個學生:

struct Stu
{
char name[20];//名字
int age;//年齡
char sex[5];//性別
char id[20];//學號
};//分號不能丟

1.3 特殊的宣告

在宣告結構的時候,可以不完全的宣告,
比如

//匿名結構體型別
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;

上面的兩個結構在宣告的時候省略掉了結構體標簽(tag),
那么問題來了?

//在上面代碼的基礎上,下面的代碼合法嗎?
p = &x;

警告:
編譯器會把上面的兩個宣告當成完全不同的兩個型別,
所以是非法的,

1.4 結構的自參考

在結構中包含一個型別為該結構本身的成員是否可以呢?

//代碼1
struct Node
{
int data;
struct Node next;
};
//可行否?

這樣當然不可以了,跟遞回類似,自己中無線包含自己,是不是有點無窮盡也的意味了,

正確的自參考方式:

//代碼2
struct Node
{
int data;
struct Node* next;
};

敲重點,注意了,

//代碼3
typedef struct
{
int data;
Node* next;
}Node;
//這樣寫代碼,可行否?

咋一看好像沒啥問題是吧,但小心了,這里我們的Node*next在定義之前就使用了

è??¥??????è????¥?????é??????è?¨???? - é??????è?-?°??±è?¨??_???è?¨?


正確的定義方式當然是下面的了

//解決方案:
typedef struct Node
{
int data;
struct Node* next;
}Node;

1.5 結構體變數的定義和初始化

有了結構體型別,那如何定義變數,其實很簡單,

struct Point
{
int x;
int y;
}p1; //宣告型別的同時定義變數p1


struct Point p2; //定義結構體變數p2


//初始化:定義變數的同時賦初值,
struct Point p3 = {x, y};


struct Stu //型別宣告
{
char name[15];//名字
int age; //年齡
};


struct Stu s = {"zhangsan", 20};//初始化


struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //結構體嵌套初始化


struct Node n2 = {20, {5, 6}, NULL};//結構體嵌套初始化

1.6 結構體記憶體對齊

結構體記憶體對齊也算是結構體復雜點之一吧, 其實理解了結構體記憶體對齊規則,問題就迎刃而解了.

首先得掌握結構體的對齊規則:
1. 第一個成員在與結構體變數偏移量為0的地址處,
2. 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處,
對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值, VS中默認的值為8
3. 結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍
4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整
體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍,

我們用圖片來更好理解

我們舉個簡單的例子

//例如:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};

我們再來分析上面兩個例子

首先我們來看S1

我們再來看S2

因為char型別的對齊數是1,int型別是4

但同時我們也要特別注意

并不是缺多少都能在空里面補多少

例如下面這種

為什么存在記憶體對齊?
大部分的參考資料都是如是說的:
1. 平臺原因(移植原因):
不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特
定型別的資料,否則拋出硬體例外,

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

那在設計結構體的時候,我們既要滿足對齊,又要節省空間,如何做到:

讓占用空間小的成員盡量集中在一起,
通過上面的例子我們已經知道原因了.

//例如:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
//S1和S2型別的成員一模一樣,但是S1和S2所占空間的大小有了一些區別,
//這里當然是第二種更省空間了

1.7 修改默認對齊數

之前我們見過了 #pragma 這個預處理指令,這里我們再次使用,可以改變我們的默認對齊數,

#include <stdio.h>
#pragma pack(8)//設定默認對齊數為8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消設定的默認對齊數,還原為默認
#pragma pack(1)//設定默認對齊數為1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消設定的默認對齊數,還原為默認
int main()
{
//輸出的結果是什么?
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}

對齊數為1,所以可以 一個接一個儲存

結論:
結構在對齊方式不合適的時候,我么可以自己更改默認對齊數,

1.8 結構體傳參

直接看代碼

struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//結構體傳參
void print1(struct S s)
{
printf("%d\n", s.num);
}
//結構體地址傳參
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //傳結構體
print2(&s); //傳地址
return 0;
}

上面的 print1 和 print2 函式哪個好些?
答案是:首選print2函式,
原因:
函式傳參的時候,引數是需要壓堆疊,會有時間和空間上的系統開銷,
如果傳遞一個結構體物件的時候,結構體過大,引數壓堆疊的的系統開銷比較大,所以會導致性能
的下降

結論:
結構體傳參的時候,要傳結構體的地址

2. 位段

2.1 什么是位段

位段的宣告和結構是類似的,有兩個不同:
1.位段的成員必須是 int、unsigned int 或signed int ,
2.位段的成員名后邊有一個冒號和一個數字,

比如:

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

A就是一個位段型別,
那位段A的大小是多少?

2.2 位段的記憶體分配

1. 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)型別
2. 位段的空間上是按照需要以4個位元組( int )或者1個位元組( char )的方式來開辟的,
3. 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程式應該避免使用位段,

//一個例子
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//空間是如何開辟的?

2.3 位段的跨平臺問題

1. int 位段被當成有符號數還是無符號數是不確定的,
2. 位段中最大位的數目不能確定,(16位機器最大16,32位機器最大32,寫成27,在16位機
器會出問題,
3. 位段中的成員在記憶體中從左向右分配,還是從右向左分配標準尚未定義,
4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是
舍棄剩余的位還是利用,這是不確定的

總結:
跟結構相比,位段可以達到同樣的效果,但是可以很好的節省空間,但是有跨平臺的問題存在,

2.4 位段的應用

3. 列舉

列舉顧名思義就是一一列舉,
把可能的取值一一列舉,
比如我們現實生活中:
一周的星期一到星期日是有限的7天,可以一一列舉,
性別有:男、女、保密,也可以一一列舉,
月份有12個月,也可以一一列舉
這里就可以使用列舉了

3.1 列舉型別的定義

enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性別
{
MALE,
FEMALE,
SECRET
};
enum Color//顏色
{
RED,
GREEN,
BLUE
};

以上定義的 enum Day , enum Sex , enum Color 都是列舉型別,
{}中的內容是列舉型別的可能取值,也叫 列舉常量
這些可能取值都是有值的,默認從0開始,一次遞增1,當然在定義的時候也可以賦初值.

例如:

enum Color//顏色
{
RED=1,
GREEN=2,
BLUE=4
};

3.2 列舉的優點

為什么使用列舉?
我們可以使用 #define 定義常量,為什么非要使用列舉?
列舉的優點:
1. 增加代碼的可讀性和可維護性
2. 和#define定義的識別符號比較列舉有型別檢查,更加嚴謹,
3. 防止了命名污染(封裝)
4. 便于除錯
5. 使用方便,一次可以定義多個常量

3.3 列舉的使用

enum Color//顏色
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//只能拿列舉常量給列舉變數賦值,才不會出現型別的差異,
clr = 5; //這種不允許

相關操作
列舉變數的值只能取列舉常量表中所列的值,就是整型數的一個子集,
列舉變數占用記憶體的大小與整型數相同,
列舉變數只能參與賦值和關系運算以及輸出操作,參與運算時用其本身的整數值,例如,設有定義:
enum color_set1 {RED, BLUE, WHITE, BLACK} color1, color2;
enum color_set2 { GREEN, RED, YELLOW, WHITE} color3, color4;
則允許的賦值操作如下:
color3=RED; //將列舉常量值賦給列舉變數
color4=color3; //相同型別的列舉變數賦值,color4的值為RED
int i=color3; //將列舉變數賦給整型變數,i的值為1
int j=GREEN; //將列舉常量賦給整型變數,j的值為0
允許的關系運算有:==、<、>、<=、>=、!=等

例如:
//比較同型別列舉變數color3,color4是否相等
if (color3==color4) cout<<”相等”;
//輸出的是變數color3與WHITE的比較結果,結果為1
cout<< color3<WHITE;
列舉變數可以直接輸出,輸出的是變數的整數值,例如:
cout<< color3; //輸出的是color3的整數值,即RED的整數值


4. 聯合(共用體)

4.1 聯合型別的定義

聯合體union的定義方式與結構體一樣,但是二者有根本區別,

在結構中各成員有各自的記憶體空間,一個結構變數的總長度是各成員長度之和,

而在“聯合”中,各成員共享一段記憶體空間,一個聯合變數的長度等于各成員中最長的長度,

聯合也是一種特殊的自定義型別
這種型別定義的變數也包含一系列的成員,特征是這些成員公用同一塊空間(所以聯合也叫共用體),
比如:

//聯合型別的宣告
union Un
{
char c;
int i;
};
//聯合變數的定義
union Un un;
//計算連個變數的大小
printf("%d\n", sizeof(un));

4.2 聯合的特點

聯合的成員是共用同一塊記憶體空間的,這樣一個聯合變數的大小,至少是最大成員的大小(因為
聯合至少得有能力保存最大的那個成員)

union Un
{
int i;
char c;
};
union Un un;
// 下面輸出的結果是一樣的嗎
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
//下面輸出的結果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);

從以上可以看出 是同一塊空間

4.3 聯合大小的計算

在The C Programming Language里面講述union記憶體分配的原話是

1)聯合體就是一個結構

2)聯合體的所有成員相對于基地址的偏移量為0

3)此結構空間要大到總夠容納最“寬”的成員

4)并且,其對齊方式要適合于聯合體中所有型別的成員

大概意思:

聯合的大小至少是最大成員的大小,
當最大成員大小不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍.

這里我們簡單看個例子.至于對齊的規則上文已經給出,這里就不多加解釋了.


以上就是筆者對自定義型別的總結,由于筆者水平有限,文章若有不足之處,還請大家在評論區留言.

同時感謝大家的支持.

覺得文章寫得不錯的,能不能給個筆者一鍵三連呢?

謝謝大家!

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

標籤:其他

上一篇:【計算機網路】物理層(Physical Layer)

下一篇:使用 Docker 搭建離線數倉環境

標籤雲
其他(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