[C語言]自定義類型詳解:結構體、聯合體、枚舉

目錄

🚀結構體

🔥結構體類型的聲明

🔥結構的自引用

🔥結構體變量的定義和初始化

🔥結構體內存對齊

🔥結構體傳參

🔥結構體實現位段(位段的填充&可移植性)

🚀枚舉

🔥枚舉類型的定義

🔥枚舉的優點

🔥枚舉的使用

🚀聯合(共用體)

🔥聯合聯合類型的定義

🔥聯合的特點

🔥聯合大小的計算



🚀結構體

🔥結構體類型的聲明

結構是一些值的集合,這些值成為成員變量。結構的每個成員可以是不同類型的變量。

結構的聲明:

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

//struct——>結構體關鍵字;Stu——>結構體標簽這里表示的是學生屬性

特殊聲明:

//匿名結構體類型
struct
{
?? ?int a;
?? ?char b;
}x;//匿名結構體類型只能使用一次
struct
{
?? ?int a;
?? ?char b;
}a[20],* p;//p指向的是這個結構體指針的地址
int main()
{
?? ?p = &x;
?? ?return 0;
}?//編譯器會把上面兩個聲明當成兩個完全不同的類型

🔥結構的自引用

在結構體中包含一個類型為該結構本身的成員

struct Node
{
?? ?int data;
?? ?struct Node next;
};

這種類型的結構自引用是非法的,成員next中又會包含一個struct Node的結構,如此遞歸下去永無止境。在計算sizeof(struct Node)時無法求出。合法聲明:

struct Node
{
?? ?int data;
?? ?struct Node* next;
};//也就是說線性結構中鏈表,每個節點包括了這個節點的數據和指向下一節點的地址的指針的信息。即數據域和指針域。

?當使用typedef類型定義和自引用時要注意下面這種陷阱:

typedef struct
{
?? ?int data;
?? ?Node* next
}Node;//匿名結構體類型,typedef重定義一個名字Node

這種寫法是非法的,因為類型名知道整個定義結束才遇到,在結構體內部的Node是未定義的

//解決方案:

typedef struct Node
{
?? ?int data;
?? ?struct Node* next
}Node;?

🔥結構體變量的定義和初始化

struct Point
{
?? ?int x;
?? ?int y;
}s1; ? ? ? ? //聲明類型的同時定義變量s1
struct Point s2;//定義結構體變量s2

//結構體嵌套初始化

struct Score
{
?? ?int n;
?? ?char ch
};
struct Stu
{
?? ?char name[20];
?? ?int age;
?? ?struct Score s
};
int main()
{
?? ?struct Stu s1 = { "zhangsan",20,{20,'q'} };
}?

🔥結構體內存對齊

如何來計算結構體的大小

#include<stdio.h>
struct S1
{char a;int i;char b;
};
struct S2
{char a;char b;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

明明只是調換了一下位置,為什么所占字節大小會不同呢?

結構體的對齊規則:

1、第一個成員在與結構體變量偏移量為0的地址處

2、其他成員變量要對齊到某個數字(對其數)的整數倍的地址處。

? ? ? 對其數=編譯器默認的一個對其數與該成員大小的較小值。

? ? ? ? ? ?vs中默認的值是8

3、結構體總體大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。

4、如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。?

為什么會存在結構體內存?

1、平臺原因:

不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則會拋出硬件異常。

2、性能原因:

數據結構(尤其是棧)應盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要做兩次內存訪問;而對齊的內存訪問僅需要一次訪問。

結構體的內存對齊是拿空間來換取時間的做法。?

設計結構體的時候,我們既要滿足對齊又要滿足節省空間,盡量把小的類型集中在前面,從而減少空間的浪費

修改默認對其數

#pragma pack(8)//設置默認對齊數為8
struct S1
{
?? ?char a;
?? ?int i;
?? ?char b;
};
#pragma pack()//取消設置的默認對齊數,還原為默認

#pragma pack(1)//設置默認對齊數為1
struct S2
{
?? ?char a;
?? ?int i;
?? ?char b; ?
};

建議不要隨意修改,當我們不去追求效率,而是追求空間浪費最少時可以考慮修改默認對齊數。

🔥結構體傳參

#include<stdio.h>
struct S
{int data[100];int num;
};
void print1(struct S ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss.data[i]);}printf("%d", ss.num);
}
void print2(struct S* ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss->data[i]);}printf("%d", ss->num);
}
int main()
{struct S s = { {1,2},100 };print1(s);//傳值調用print2(&s);//傳址調用return 0;
}

?在進行結構體傳參時我們傳地址是更好的,這是因為函數傳參的時候,參數是需要壓棧的,會有時間和空間上的系統開銷。如果傳遞一個結構體對象的時候,結構以過大,參數壓棧的系統開銷比較大,會導致性能的下降。

🔥結構體實現位段(位段的填充&可移植性)

位段(位指的是比特位)的聲明和結構是類似的,有兩個不同:

1、位段的成員只能是整型:int、unsigned int,signed int或者char類型的

2、位段的成員后面有一個冒號和數字

#include<stdio.h>
struct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};

位段的內存分配

1、位段成員可以是int 、unsigned int、signed int或者char類型

2、位段的空間上是按照需要以4(int)個字節或 1(char)個字節的方式來開辟的

3、位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應避免使用位段

//舉個栗子:

#include<stdio.h>

struct S
{
?? ?//先開辟一個字節的空間,8個比特位
?? ?char a : 3;
?? ?//用了3個還剩5個比特位
?? ?char b : 4;
?? ?//用了4個還剩1個比特位
?? ?char c : 5;
?? ?//不夠用了,再開辟一個字節,8個比特位,用了5個,剩下3個
?? ?char d : 4;
?? ?//又不夠用了,再開辟一個字節,8個比特位用了4個,剩下4個
};
int main()
{
?? ?struct S s = { 0 };
?? ?printf("%d\n", sizeof(struct S));//3
?? ?s.a = 10;
?? ?s.b = 12;
?? ?s.c = 3;
?? ?s.d = 4;
?? ?return 0;
}

?

?調試一走,我們發現vs的規則和我們計算的是沒有差別的

但我們在使用位段的時候會有很多問題:

1、int位段被當成有符號數還是無符號數是不確定的

2、位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題。

3、位段中的成員在內存中從左向右分配,還是從右向左分配標準未定義。

4、當一個結構中包含兩個位段,第二個位段的成員比較大,無法容納容納于第一個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的

總結:與結構相比,位段可以達到同樣效果,但是可以很好的節省空間,但是有跨平臺的問題存在。?當然位段在計算機網絡中有其獨有的作用,能節省不少空間浪費(數據越少,狀態越好),從而達到網絡環境較優的狀態

🚀枚舉

枚舉顧名思義就是一一列舉,比如:一年12個月可以一一列舉、一周7天可以一一列舉。

🔥枚舉類型的定義

enum Day//星期
{
?? ?Mon,
?? ?Tues,
?? ?Wed
?? ?Thur,
?? ?Fri,
?? ?Sat,
?? ?Sun
};

enum Sex//性別
{
?? ?MALE,
?? ?FEMALE,
?? ?SECRET
};//與結構體是非常相似的,但其內部是用逗號分隔開的,且內部只包含符號

{ }里面的內容是枚舉類型的可能取值,也叫枚舉常量。

這些可能取值都是有值的,默認從0開始,一次遞增一,當然在定義的時候也可以賦初值。

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

🔥枚舉的優點

我們可以使用 #define 定義常量,為什么要用枚舉呢?

1、增加代碼的可讀性和可維護性

2、和#define定義的標識符比較枚舉有類型檢查,更加嚴謹

3、防止命名污染(封裝)

4、便于調試

5、使用方便,一次可以定義多個常量

🔥枚舉的使用

enum Color//顏色
{RED = 1,GREEN=2,BLUE=4
};
int main()
{enum Color clr = GREEN;//只能拿枚舉常量給枚舉變量賦值,才不會出現類型的差異clr = 5;return 0;
}

🚀聯合(共用體)

🔥聯合聯合類型的定義

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

//舉例:

union Un
{
?? ?int a;
?? ?char c;
};
int main()
{
?? ?union Un u;
?? ?printf("%d\n", sizeof(u));//4,說明共用了一份空間
?? ?return 0;
}

?從這里我們就能看出來a與c共用一份空間

🔥聯合的特點

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

union Un
{int a;char c;
};
int main()
{union Un u;u.a = 0x11223344;u.c = 0x55;printf("%x\n", u.a);return 0;
}

?判斷機器是大端存儲還是小端存儲(用聯合的方法)

int check_sys()
{union Un{int a;char b;}u;u.a = 1;return u.b;
}
int main()
{int ret = check_sys();if (ret == 1){printf("小端\n");}elseprintf("大端\n");return 0;
}

🔥聯合大小的計算

·聯合的大小至少是最大成員的大小。

·當最大成員大小不是最大對齊數的整數倍時,就要對齊到最大對齊數的整數倍。

union Un1
{
?? ?char c[5];
?? ?int i;
};//本來應該是5,最大對齊數為4,所以大小為4的倍數即8

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/15786.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/15786.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/15786.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安裝 Ubuntu桌面版,詳細步驟(附引導 U盤制作工具)

下載鏡像 安裝Ubuntu首先要下載鏡像包&#xff0c;訪問下面網址下載鏡像包 https://releases.ubuntu.com/ 選擇你要安裝的Ubuntu版本 將 .iso 文件保存到所需位置&#xff0c;下面會使用此文件創建可引導 U盤。 制作 Ubuntu 引導 U 盤 首先要找到一個大于4G的U盤&#xff…

貓頭虎 解析:為什么AIGC在國內適合做TOB,在國外適合做TOC?

貓頭虎 解析&#xff1a;為什么AIGC在國內適合做TOB&#xff0c;在國外適合做TOC&#xff1f; 博主 貓頭虎 的技術世界 &#x1f31f; 歡迎來到貓頭虎的博客 — 探索技術的無限可能&#xff01; 專欄鏈接&#xff1a; &#x1f517; 精選專欄&#xff1a; 《面試題大全》 — 面…

ps進程查看命令詳解

1、PS 命令是什么 查看它的man手冊可以看到&#xff0c;ps命令能夠給出當前系統中進程的快照。它能捕獲系統在某一事件的進程狀態。如果你想不斷更新查看的這個狀態&#xff0c;可以使用top命令。 2、ps命令支持三種使用的語法格式 UNIX 風格&#xff0c;選項可以組合在一起…

鴻蒙ArkUI-X平臺差異化:【運行態差異化(@ohos.deviceInfo)】

平臺差異化 簡介 跨平臺使用場景是一套ArkTS代碼運行在多個終端設備上&#xff0c;如Android、iOS、OpenHarmony&#xff08;含基于OpenHarmony發行的商業版&#xff0c;如HarmonyOS Next&#xff09;。當不同平臺業務邏輯不同&#xff0c;或使用了不支持跨平臺的API&#xf…

c++中靜態函數

在Qt中&#xff0c;可以通過在類中定義靜態方法來添加靜態方法。靜態方法是類的一部分&#xff0c;但不需要實例化類對象就可以直接調用。 下面是一個示例&#xff0c;演示了如何在類中添加靜態方法&#xff1a; cpp class MyClass { public: static void myStaticMethod…

Postman快捷功能-批量斷言與快速查詢替換

大家好&#xff0c;在我們日常的接口測試工作中&#xff0c;經常需要對接口返回的數據進行斷言&#xff0c;以確保接口的正確性。當接口數量較多時&#xff0c;逐個編寫斷言語句會變得非常繁瑣。此外&#xff0c;在接口測試過程中&#xff0c;我們還可能需要頻繁地查找和替換某…

Python自動化工具(桌面自動化、Web自動化、游戲輔助)

工具介紹 連點工具是一款可以模擬鍵鼠后臺操作的連點器工具。支持鼠標連點、鍵鼠腳本錄制&#xff0c;支持輔助您實現辦公自動化以及輔助游戲操作。功能簡潔易用&#xff0c;非常方便操作。連點工具讓您在在玩游戲、網購搶購的時候全自動點擊鼠標&#xff01;主要功能有&#…

MySQL數據庫的數據文件保存在哪?MySQL數據存在哪里

在安裝好MySQL數據庫使用一段時間后&#xff0c;會產生許多的數據庫和數據。那這些數據庫的數據文件存放在本地文件夾的什么位置呢 一、默認位置 一般來說MySQL數據庫的數據文件都是存放在data文件夾之中&#xff0c;但是根據使用的存儲引擎不同&#xff0c;產生的一些文件也…

牛客前端面試高頻八股總結(2)(附文檔)

1.fetch請求方式 fetch是什么&#xff1f; fetch是一種http 數據請求的方式&#xff0c;是xml的一種替代方式。 怎么請求&#xff1f; fetch方法返回一個promise解析response顯示狀態&#xff08;成功與否&#xff09;的方法。 優點&#xff1a; 使用Promise&#xff0c;支持鏈…

24李林跌落神壇,880還刷嗎?還是換1000、900、660?

“李林今年跌落神壇了&#xff01;” “全是固定題型沒新題&#xff0c;結果今年考的全是新題。” 880是“老真題的神”&#xff0c; 遇到24年&#xff0c;冷門考點多&#xff0c;計算量又大&#xff0c;就不靈了。 但“老真題”&#xff0c;還是得刷。就像往年真題是要刷的…

(十一)統計學基礎練習題五(50道選擇題)

本文整理了統計學基礎知識相關的練習題&#xff0c;共50道&#xff0c;適用于想鞏固統計學基礎或備考的同學。來源&#xff1a;如荷學數據科學題庫&#xff08;技術專項-統計學二&#xff09;。序號之前的題請看往期文章。 201&#xff09; 202&#xff09; 203&#xff09; 2…

得帆信息PMO總監李健達受邀為第十三屆中國PMO大會演講嘉賓

全國PMO專業人士年度盛會 上海得帆信息技術有限公司aPaaS業務線副總裁、PMO總監李健達先生受邀為PMO評論主辦的2024第十三屆中國PMO大會演講嘉賓&#xff0c;演講議題為“AI時代的PMO工作法”。大會將于6月29-30日在北京舉辦&#xff0c;敬請關注&#xff01; 議題簡要&#x…

39. 組合總和 - 力扣(LeetCode)

基礎知識要求&#xff1a; Java&#xff1a; 方法、集合、泛型、Arrays工具類、for循環、if判斷 Python&#xff1a; 方法、列表、for循環、if判斷 題目&#xff1a; 給你一個 無重復元素 的整數數組 candidates 和一個目標整數 target &#xff0c;找出 candidates 中可以使…

Spring框架學習筆記(五):JdbcTemplate 和 聲明式事務

基本介紹&#xff1a;通過 Spring 框架可以配置數據源&#xff0c;從而完成對數據表的操作。JdbcTemplate 是 Spring 提供的訪問數據庫的技術。將 JDBC 的常用操作封裝為模板方法 1 JdbcTemplate 使用前需進行如下配置 1.1 在maven項目的pom文件加入以下依賴 <dependencies…

Java面試進階指南:高級知識點問答精粹(二)

Java 面試問題及答案 1. 什么是Java內存模型&#xff08;JMM&#xff09;&#xff1f;它在并發編程中扮演什么角色&#xff1f; 答案&#xff1a; Java內存模型&#xff08;JMM&#xff09;是一個抽象的模型&#xff0c;它定義了Java程序中各種變量&#xff08;線程共享變量&…

labelme的使用

創建虛擬環境 聽說是要用這個3.6版本的python環境 conda create --namelabelme python3.6激活虛擬環境 activate labelme下載labelme pip install labelme #安裝labelme組件啟動labelme 在你打開文件的時候推薦還是自己先建立一個label.txt 把自己要分的類別放進去 label.…

Python中的深拷貝與淺拷貝:深入解析與實用指南

Python中的深拷貝與淺拷貝&#xff1a;深入解析與實用指南 一、引言 在Python編程中&#xff0c;我們經常需要復制對象&#xff0c;但有時候僅僅復制對象的引用是不夠的&#xff0c;我們需要的是對象的真實副本。此時&#xff0c;我們就需要考慮使用深拷貝或淺拷貝。深拷貝和…

GPT-2添加PAD token

GPT-2和GPT-3模型&#xff08;包括其他類似系列&#xff09;通常沒有內置的PAD token&#xff0c;因為它們主要用于生成任務&#xff0c;而這些任務通常不需要填充。然而&#xff0c;在一些特定任務&#xff08;如批量處理或序列對齊&#xff09;中&#xff0c;添加PAD token是…

翻譯《The Old New Thing》- What‘s the deal with the EM_SETHILITE message?

Whats the deal with the EM_SETHILITE message? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071025-00/?p24693 Raymond Chen 2007年10月25日 簡要 文章討論了EM_SETHILITE和EM_GETHILITE消息在文檔中顯示為“未實現”的原因。這些…

前端 JS 經典:Web 性能指標

什么是性能指標&#xff1a;Web Performance Metrics 翻譯成 Web 性能指標&#xff0c;一般和時間有關系&#xff0c;在短時間內做更多有意義的事情。 一個站點表現得好與不好&#xff0c;標準在于用戶體驗&#xff0c;而用戶體驗好不好&#xff0c;有一套 RAIL 模型來衡量。這…