【C語言】結構體,枚舉,聯合超詳解!!!

目錄

結構體

結構體聲明

結構體成員的訪問

結構體自引用?

結構體變量定義,初始化,傳參?

結構體內存對齊?

位段

枚舉

聯合(共用體)


結構體

結構體聲明

1. 概念

1. 結構體是一些值的集合,這些值稱為成員變量。

2. 結構體的每個成員可以是不同類型的變量。

3. 數組:一組相同類型元素的集合,結構體:一組不一定相同類型元素的集合。

4.?結構體的成員可以是標量、數組、指針,甚至是其他結構體。

2. 聲明

例子

假設我們要用結構體表示一個學生類型

struct Stu
{   char name[20]; //名字int age; //年齡char sex[5]; //性別
};

我們還可以這樣寫,這樣表示直接用這個結構體類型創建s1和s2變量。

struct Stu
{   char name[20]; int age; char sex[5]; 
}s1, s2;

也可以這樣創建變量。

struct Stu s3;

2. 特殊的聲明?

在聲明結構的時候,可以不完全的聲明。

比如:匿名結構體類型。

接下來我這樣寫。

struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}*p;

請問我可以寫 p = &x; 嗎?答案是不行。雖然成員是一樣的,但編譯器會把上面的兩個聲明當成完全不同的兩個類型。


結構體成員的訪問

1. 結構體變量訪問成員

結構體變量的成員是通過點操作符(.)訪問的。點操作符接受兩個操作數。

比如:

struct Stu
{char name[20];int age;
};struct Stu s;
strcpy(s.name, "zhangsan");//使用.訪問name成員
s.age = 20;//使用.訪問age成員

2. 結構體指針訪問成員?

有時候我們得到的不是一個結構體變量,而是指向一個結構體的指針,那該如何訪問成員?

struct Stu
{char name[20];int age;
};void print(struct Stu* ps)
{printf("name = %s   age = %d\n", (*ps).name, (*ps).age);//使用結構體指針訪問指向對象的成員printf("name = %s   age = %d\n", ps->name, ps->age);
}int main()
{struct Stu s = {"zhangsan", 20};print(&s);//結構體地址傳參return 0;
}

結構體自引用?

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

比如這樣寫可以嗎?

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

答案是不行,因為無法計算struct Node有多大,無限套娃。

正確寫法:

//結構體的自引用
struct Node
{int data;struct Node* next;
};

2.?搭配 typedef 的寫法?

錯誤寫法:

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

正確寫法:

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

這里的執行邏輯是我們先對這個類型重命名之后產生Node,也就是說重命名之前的寫法必須是正確的。


結構體變量定義,初始化,傳參?

1. 定義,初始化

有了結構體類型,那如何定義變量呢?如何初始化呢?

有兩個地方可以定義變量。定義變量的同時也能初始化。

struct Point
{int x;int y;
}p1 = {1, 2}; //第一種方法,這里創建的變量是全局變量,同時也能初始化               int main()
{struct Point p2 = {3, 4}; //第二種方法,這里創建的變量是局部變量,同時初始化struct Point p3 = {.y=5, .x=6}; //這里用了結構成員訪問符,可以不按順序初始化return 0;
}

2. 嵌套初始化?

我們想一個問題,結構體里面有沒有可能出現結構體類型的數據呢?答案是有可能的。

比如:

struct Point
{int x;int y;
};struct PP
{double d;struct Point pt;int a;
}

那么這個怎么初始化呢?

很簡單,嵌套的那個結構體自己加一對大括號就好了。

int main()
{struct PP p = {3.14, {1, 2}, 8};printf("%d\n", p.pt.x); //想要訪問嵌套的結構體只需用多一次 . 即可    return 0;
}

3. 結構體傳參

下方代碼中傳結構體與傳結構體地址哪個好一些?

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

答案:傳結構體地址。

原因:函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。 如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。


結構體內存對齊?

1.?計算結構體的大小

想了解內存對齊我們得先思考下面這個例子

struct S1
{char c1; //1int i; //4char c2; //1
};struct S2{char c1; //1char c2; //1int i; //4};int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

經過分析我們認為都是6,因為1+1+4嘛,然而結果是S1大小為12,S2大小為8。為什么?

因為這里面就涉及到結構體內存對齊了。

2.?結構體的對齊規則

1. 結構體的第一個成員永遠放在與結構體變量起始位置偏移量為0的位置。

2. 從第二個成員開始,往后每個成員都要對齊到對齊數的整數倍處。對齊數 = 編譯器默認的一個對齊數與該成員大小的較小值。VS中默認的值為8。Linux中沒有默認對齊數,對齊數就是成員自身的大小。

3. 結構體總大小為最大對齊數的整數倍。最大對齊數是所有成員的對齊數的最大值。

4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,

結構體的總大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

3. 練習?

struct S3
{double d;char c;int i;
};printf("%d\n", sizeof(struct S3));

答:16

解析:1. d的對齊數是8,但因為d是第一個成員所以直接放在偏移量為0的位置,d是8個字節占用偏移量0到7的位置。

2. c的對齊數是1,此時偏移量為8是1(對齊數)的整數倍,c是1個字節占用偏移量為8的位置。

3. i的對齊數是4,此時偏移量為9不是4(對齊數)的整數倍,一直往后到偏移量12的位置,此時是4(對齊數)的整數倍,i是4個字節占用12到15的位置。

4. 偏移量從0到15,所以總大小為16,16是8(最大對齊數)的整數倍。

.

下面這個結構體嵌套了結構體,這大小該如何計算呢?

struct S4
{char c1;struct S3 s3;double d;
};printf("%d\n", sizeof(struct S4));

答:32

解析:1. c1對齊數是1,因為是第一個成員所以放在偏移量為0的位置,c1是一個字節所以占用一個位置。

2. s3是結構體所以s3的對齊數是自己成員的最大對齊數是8,此時的偏移量是1不是對齊數的整數倍所以一直移到偏移量為8的位置,s3是16個字節占用8到23的位置。

3. d的對齊數是8,此時偏移量為24是對齊數的整數倍,d是8個字節占用24到31的位置。

4. 偏移量從0到31一個32個位置是所有最大對齊數的整數倍。

4. 為什么存在內存對齊??

1. 平臺原因(移植原因): 不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。

2. 性能原因: 數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。

原因在于為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。 總體來說: 結構體的內存對齊是拿空間來換取時間的做法。

.

那我們如何設計結構體既能滿足對齊又能節省空間呢?

答案:讓占用空間小的成員盡量集中在一起。

比如下面代碼,S2就比S1省空間。

struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};

5. 修改默認對齊數

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

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


位段

1. 什么是位段

位段的成員名后邊有一個冒號和一個數字。

比如:A就是一個位段類型。

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

那位段A的大小是多少?

printf("%d\n", sizeof(struct A));

答案:8字節

因為位段的位是二進制位,意味著_a占兩個比特位,_b占5個比特位,_c占10個比特位,_d占30個比特位,一個是47個比特位,一個int32字節不夠,所有給了兩個int。

.

那有同學要問了,為什么要弄位段呢?

其實有時候我們設計結構體成員的時候,它的取值非常有限,可能只會用到幾個比特位,剩下的比特位給其他成員用,這樣可以節省空間。

2. 位段的內存分配?

1. 位段的成員可以是 int,unsigned int,signed int 或者是char (屬于整形家族)類型。

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

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

.

這里的不確定因素包括:

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

2. 當一個位段成員占用比特位比較大需要開辟新空間時,前面空間剩余的比特位是使用還是舍棄不確定。

3. int 位段被當成有符號數還是無符號數是不確定的。

4. 位段中最大位的數目不能確定(16位機器最大16,32位機器最大32)。

不確定就會導致不同的平臺有不同的實現。

3. VS環境位段的內存分配?

這個62 03 04是如何來的呢?請聽我細細道來。

第一步,根據每個成員后面的數字分配比特位,在VS中位段的內存分配是從右到左的。并且,當需要開辟新空間時,前面剩余的比特位不使用。

第二步,根據上面代碼將數值轉成二進制初始化,s.a=10,比特位是1010,但a只有三個比特位,所以高位的1會舍去。

第三步,轉成十六進制,四個比特位轉一個十六進制數。

4. 位段的應用?


枚舉

1. 枚舉類型的定義

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

1. 以上定義的 enum Day,enum Sex,enum Color 都是枚舉類型。

2.?{ }中的內容是枚舉類型的可能取值,也叫枚舉常量。

3.?這些可能取值都是有值的,默認從0開始,依次遞增1,在聲明枚舉類型的時候也可以賦初值。比如:

enum Color
{RED=1,GREEN=8,BLUE
};

這個BLUE的值是在前面的基礎上遞增1,也就是9。

2. 枚舉的使用

只能拿枚舉常量給枚舉變量賦值,才不會出現類型的差異。

enum Color
{RED=1,GREEN=2,BLUE=4
};enum Color clr = GREEN;

3.??枚舉的優點

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

枚舉的優點:

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

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

3. 便于調試。

4. 使用方便,一次可以定義多個常量。


聯合(共用體)

1.?聯合類型的定義

1. 聯合是一種特殊的自定義類型。

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

例子:

//聯合類型的聲明
union Un
{char c;int i;
};//聯合變量的定義
union Un un;

2.?聯合的特點?

1. 聯合的成員是共用同一塊內存空間的。

2. 一個聯合變量的大小,至少是最大成員的大小,因為聯合至少得有能力保存最大的那個成員。

例子:

我們可以看出,這個聯合類型大小為4且成員的地址都相同。

如圖,i 和 c 共用同一塊空間。

.

所以當我們修改 c 的時候,i 也會跟著變。

3.??面試題:判斷當前計算機的大小端存儲

之前我們學到用指針來判斷,這次我們利用聯合判斷。

思路:i = 1 有兩種存儲字節序,一種是 00 00 00 01,一種是 01 00 00 00,所以我們就對比第一個字節即可。

int main()
{union {char c;int i;}un = {.i = 1};//判斷第一個字節是1還是0if ((int)un.c) printf("small\n");else printf("big\n");return 0;
}

4.??聯合大小的計算

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

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

例子1:

union Un1
{char c[5];int i;
};

答:Un1大小是8字節。

解析:首先c是char數組大小為5,i大小為4,那么Un1大小至少為5,然后我們看5是不是最大對齊數的整數倍,c的對齊數是1因為c的元素類型是char,i的對齊數是4,所以總大小要從5提升到8。

.

例子2

union Un2
{short c[7];int i;
};

答:16字節。

解析:第一步,c的大小為14,i的大小為4,那么可以確定Un2的大小至少為14。第二步,判斷14是不是最大對齊數的整數倍,很明顯14不是4的整數倍,所以14增加到16。

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

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

相關文章

長難句打卡7.15

The trend was naturally most obvious in those areas of science based especially on a mathematical or laboratory training, and can be illustrated in terms of the development of geology in the United Kingdom 這一趨勢自然在以數學或實驗室訓練為基礎的科學領域里…

Unlink

Unlink 原理 我們在利用 unlink 所造成的漏洞時,其實就是對 chunk 進行內存布局,然后借助 unlink 操作來達成修改指針的效果。簡單回顧一下 unlink 的目的與過程,其目的是把一個雙向鏈表中的空閑塊拿出來(例如 free 時和目前物理…

Leetcode二分搜索法淺析

文章目錄 1.二分搜索法1.1什么是二分搜索法?1.2解法思路 1.二分搜索法 題目原文: 給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返…

從PyTorch官方的一篇教程說開去(1 - 初心)

原文在此,喜歡讀原汁原味的可以自行去跟,這是一個非常經典和有學習意義的例子,在此向老爺子們致敬 - https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html 開源文化好是好,但是“公地的悲哀”這點避不開…

每天一個數據分析題(四百三十七)- 統計量

若x1、x2、x3取自某總體的樣本,當期望a已知,方差c未知時,則以下不是統計量的是 A. x1x2x3 B. min{x1,x2,x3} C. a(x1x2)/2 D. (x1x2x3)/c 數據分析認證考試介紹:點擊進入 題目來源于CDA模擬題庫 點擊…

用c語言實現一個alexnet

在C語言中實現一個完整的AlexNet神經網絡是一個復雜的任務,涉及大量的矩陣運算和卷積操作。這個示例代碼將涵蓋AlexNet的主要組件,包括卷積層、ReLU激活函數、池化層和全連接層。 由于C語言本身沒有像Python的NumPy那樣的矩陣庫,我們需要自己編寫矩陣運算函數。另外,C語言…

高效運維:構建全面監控與自動化管理體系

在當今的數字化時代,運維管理已成為企業IT架構中不可或缺的一環。它不僅關乎系統的穩定運行,更直接影響到業務的響應速度、故障處理時間以及客戶滿意度等多個方面。因此,構建一套全面監控與自動化管理體系,對于提升企業運維效率、…

無人機之多旋翼與固定翼的區別

多旋翼無人機和固定翼無人機是無人機技術中的兩種主要形式,各自有獨特的優勢和應用場景。 一、飛行原理與結構 多旋翼無人機:依靠多個旋翼產生升力來平衡飛行器的重力,通過改變每個旋翼的轉速控制飛行器的姿態和平穩,使其能夠垂…

C++ //練習 15.22 對于你在上一題中選擇的類,為其添加合適的虛函數及公有成員和受保護的成員。

C Primer(第5版) 練習 15.22 練習 15.22 對于你在上一題中選擇的類,為其添加合適的虛函數及公有成員和受保護的成員。 環境:Linux Ubuntu(云服務器) 工具:vim 代碼塊 class Shape {public:S…

PDF文件無法編輯?3步快速移除PDF編輯限制

正常來說,我們通過編輯器打開pdf文件后,就可以進行編輯了。如果遇到了打開pdf卻不能編輯的情況,那有可能是因為密碼或是掃描件的原因。小編整理了一些pdf文件無法編輯,以及pdf文件無法編輯時我們要如何處理的方法。下面就隨小編一起來…

[word] word如何編寫公式? #微信#知識分享

word如何編寫公式? word如何編寫公式?Word中數學公式是經常會使用到的,若是要在文檔中錄入一些復雜的公式,要怎么做呢?接下來小編就來給大家講一講具體操作,一起看過來吧! 方法一:…

stm32學習:(寄存器3)系統架構

時鐘系統 時鐘樹 在STM32中有3種不同的時鐘源用來驅動系統時鐘(SYSCLK): HSI振蕩器時鐘(High Speed Internal oscillator,高速內部時鐘)HSE振蕩器時鐘(High Speed External(Oscillator / Clock&#xff…

Ruby爬蟲技術:深度解析Zhihu網頁結構

在互聯網時代,數據的價值日益凸顯,尤其是在社交媒體和問答平臺如Zhihu(知乎)上,用戶生成的內容蘊含著豐富的信息和洞察。本文將深入探討如何使用Ruby爬蟲技術來解析Zhihu的網頁結構,并獲取有價值的數據。 …

linux service小例

linux service 測試 1.創建一個app // myapp.c // 間隔10s寫入時間到文件 #include <stdio.h> #include <time.h> #include <unistd.h> // 引入unix標準函數定義&#xff0c;如sleep()int main() {FILE *fp;time_t now;char buffer[80];// 打開文件以追加模…

啊?原來你也看環法賽!—VELO Angel Glide坐墊,與你共攀環法榮耀之路!

當七月的熱浪席卷賽道&#xff0c;環法自行車賽&#xff08;Tour de France&#xff09;的戰鼓再次響起&#xff0c;挑戰與夢想交織的火花在每一寸賽道上綻放。自1903年首屆賽事以來&#xff0c;環法已成為全球最具聲望的自行車賽事&#xff0c;吸引著無數頂尖騎手和觀眾的目光…

c語言程序環境和預處理

test.c(源文件) --> 編譯器 --> test.obj(目標文件,在debug里) 鏈接庫和多個目標文件 經過 鏈接器的處理&#xff0c;最終生成可執行程序.exe 編譯階段 預處理/預編譯階段 &#xff1a;1.頭文件的包含 2.define定義符號的替換&#xff0c;并刪除定義的符號 3.刪除注釋 這…

醫學影像歸檔與通訊系統源碼,C#PACS源碼,涵蓋放射、超聲、內鏡、病理、核醫學

醫學影像歸檔與通訊系統&#xff08;PACS&#xff09;系統&#xff0c;是一套適用于從單一影像設備到放射科室、到全院級別等各種應用規模的醫學影像歸檔與通訊系統。PACS集患者登記、圖像采集、存檔與調閱、報告與打印、查詢、統計、刻錄等功能為一體&#xff0c;有效地實現了…

【保衛花果山】游戲

游戲介紹 拯救花果山是一款玩家能夠進行趣味闖關的休閑類游戲。拯救花果山中玩家需要保護花果山的猴子&#xff0c;利用各種道具來防御妖魔鬼怪的入侵&#xff0c;游戲中玩家需要面對的場景非常的多樣&#xff0c;要找到各種應對敵人的方法。拯救花果山里玩家可以不斷的進行闖…

【開源 Mac 工具推薦之 2】洛雪音樂(lx-music-desktop):免費良心的音樂平臺

舊版文章&#xff1a;【macOS免費軟件推薦】第6期&#xff1a;洛雪音樂 Note&#xff1a;本文在舊版文章的基礎上&#xff0c;新更新展示了一些洛雪音樂的新功能&#xff0c;并且描述更為詳細。 簡介 洛雪音樂&#xff08;GitHub 名&#xff1a;lx-music-desktop &#xff09;…

JavaScript學習筆記(九)

56、JavaScript 類 56.1 JavaScript 類的語法 請使用關鍵字 class 創建一個類。 請始終添加一個名為 constructor() 的方法。 JavaScript 類不是對象。 它是 JavaScript 對象的模板。 語法&#xff1a; class ClassName {constructor() { ... } }示例&#xff1a;例子創…