C語言自定義類型的保姆級講解
前言
在我們?nèi)粘懘a時,經(jīng)常會遇到結(jié)構(gòu)體類型的使用,今天帶讀者了解結(jié)構(gòu)體類型的使用。
一、初始結(jié)構(gòu)體
在了解結(jié)構(gòu)體之前,我們先來了解一下結(jié)構(gòu)體的基礎(chǔ)只是,結(jié)構(gòu)體到底是什么?
結(jié)構(gòu)是一些值的集合,這些值稱為成員變量。結(jié)構(gòu)的每個成員可以是不同類型的變量。
下面舉一個例子:
struct tag
{
menber_list; //成員列表
}variable_list; //變量列表
例如我們使用結(jié)構(gòu)體描述一臺電腦
struct computer
{
int price;//價格
char name[20];//名稱
char brand[10];//品牌
}computer; //需要注意的是最后一行的“ ;”不能丟哦,不然編譯器會報錯提示你。
結(jié)構(gòu)成員的類型
結(jié)構(gòu)成員可以是標(biāo)量數(shù)組、指針、甚至可以是其他的結(jié)構(gòu)體。
示例:pandas 是基于NumPy 的一種工具,該工具是為了解決數(shù)據(jù)分析任務(wù)而創(chuàng)建的。
匿名結(jié)構(gòu)體類型
//匿名結(jié)構(gòu)體類型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
上面的代碼中結(jié)構(gòu)體省略掉了結(jié)構(gòu)體標(biāo)簽,我們在添加一行代碼
p=&x;
當(dāng)我們編譯時,會發(fā)現(xiàn)這樣兩個錯誤。

所以這樣是不可行的。
結(jié)構(gòu)體的自引用
就像函數(shù)遞歸一樣,結(jié)構(gòu)體也可以自己引用自己。他們的格式是這樣的。
struct Node
{
int data;
struct Node* next;
};
結(jié)構(gòu)體變量的定義和初始化
struct stu
{
char name[20];
int age;
};
struct stu s = { "Geralt ",100};
利維亞的杰洛特,100歲!
結(jié)構(gòu)體內(nèi)存對齊
各成員變量存放的起始地址相對于結(jié)構(gòu)的起始地址的偏移量必須為該變量的類型所占用的字節(jié)數(shù)的倍數(shù) 各成員變量在存放的時候根據(jù)在結(jié)構(gòu)中出現(xiàn)的順序依次申請空間 同時按照上面的對齊方式調(diào)整位置 空缺的字節(jié)自動填充 同時為了確保結(jié)構(gòu)的大小為結(jié)構(gòu)的字節(jié)邊界數(shù)(即該結(jié)構(gòu)中占用最大的空間的類型的字節(jié)數(shù))的倍數(shù),所以在為最后一個成員變量申請空間后 還會根據(jù)需要自動填充空缺的字節(jié)。
簡單的說就是:結(jié)構(gòu)體的內(nèi)存對齊是拿空間來換取時間的結(jié)果,提高了效率,浪費少許空間。
規(guī)則如下:
- 第一個成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
- 其他成員變量要對齊到某個數(shù)字(對齊數(shù))的整數(shù)倍的地址處。
對齊數(shù) = 編譯器默認(rèn)的一個對齊數(shù) 與 該成員大小的較小值。 - 結(jié)構(gòu)體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。
- 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。
VS編譯器中默認(rèn)對齊數(shù)是8。
當(dāng)然了,作為創(chuàng)建VS,Linux的我們是可以在C語言中自定義對齊數(shù)的。
我們可以使用#pragma這個預(yù)處理指令修改默認(rèn)對齊數(shù)。
Eg:#pragma pack(4) //修改對齊數(shù)為4
結(jié)構(gòu)體傳參
struct s
{
int data[1000];
int num;
};
struct s s = { {1,2,3,4},100 };
//結(jié)構(gòu)體傳參
void print1(struct s s)
{
printf("%d\n", s.num);
}
//結(jié)構(gòu)體地址傳參
void print2(struct s* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s);
print2(&s);
return 0;
和函數(shù)傳參一樣,形參是實參的一份臨時拷貝,參數(shù)是需要壓棧,會有時間和空間上的系統(tǒng)開銷,如果傳遞的過程中結(jié)構(gòu)體過大,就可能會導(dǎo)致系統(tǒng)開銷大,導(dǎo)致性能的下降。我們不如直接傳過去一份地址,再對它進(jìn)行解引用操作符。
二、位段
位段與結(jié)構(gòu)體不同的地方在于聲明的時候,1.位段的成員必須是 int、unsigned int 或signed int 。2.位段的成員名后邊有一個冒號和一個數(shù)字。
舉一個例子:
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
- 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)類型
- 位段的空間上是按照需要以4個字節(jié)( int )或者1個字節(jié)( char )的方式來開辟的。
- 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應(yīng)該避免使用位段。
- 舉一個例子
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;
那么位段的空間是如何開辟的呢?一張圖搞定!
總的來說,位段和結(jié)構(gòu)體相似,可以更好的節(jié)省空間,但是位段有跨平臺的問題存在。
三、枚舉
枚舉枚舉,顧名思義就是把可能出現(xiàn)的取值一一列舉出來。
比如我們要描述一年的月份:
可以一一列舉,我們要描述計算機(jī)語言的類型,也可以一一列舉。
下面舉一個例子
代碼如下(示例):
enum ComputerLangue
{
C,
Java,
Python,
};
每種結(jié)構(gòu)體都有一定的優(yōu)點,那么枚舉有什么優(yōu)點呢?
- 增加代碼的可讀性和可維護(hù)性
- 和#define定義的標(biāo)識符比較枚舉有類型檢查,更加嚴(yán)謹(jǐn)。
- 防止了命名污染(封裝)
- 便于調(diào)試
- 使用方便,一次可以定義多個常量
聯(lián)合(公用體)
聯(lián)合的特點是成員們公用同一塊空間,因此他們也可以叫共用體
代碼如下(示例):
//聯(lián)合類型的聲明
union Un
{
char c;
int i;
};
//聯(lián)合變量的定義
union Un un;
//計算連個變量的大小
printf("%d\n", sizeof(un));
三、 練習(xí)
在VC2013這款編譯器中,這個結(jié)構(gòu)體所占的空間大小是多少字節(jié)?
typedef struct{
int a;
char b;
short c;
short d;
}AA_t;
答案是:12個字節(jié)
為什么呢?結(jié)構(gòu)體判斷大小,一般向成員中最長的元素對齊。

在這4個結(jié)構(gòu)成員中,最長的元素為a占四個字節(jié)。所以其他元素要向四對齊。a單獨占四個字節(jié),b為char類型占一個字節(jié),c為short和b一起占四個字節(jié)并且空出一個字節(jié)。剩下d占一個字節(jié),空兩個字節(jié)。所以總占空間大小是4+1+2+2+3=12字節(jié)。
struct A
{
int a;
short b;
int c;
char d;
};
struct B
{
int a;
short b;
char c;
int d;
};
我們在來看一道題 :在32位系統(tǒng)環(huán)境,編譯選項為4字節(jié)對齊,那么sizeof(A)和sizeof(B)是( )
答案是:16、12

最長的結(jié)構(gòu)成員為int 占4個字節(jié),b占兩個字節(jié),空下兩個字節(jié),c占4個字節(jié),d占一個字節(jié),空3個字節(jié)。

最長的類型為int,a占4字節(jié),b和c一起占三個字節(jié),還空下一個字節(jié),d占4字節(jié)。
所以sizeof(A)sizeof(B)分別是16 12.
總結(jié)
我們介紹了C語言中的自定義類型,結(jié)構(gòu)體,聯(lián)合,枚舉,位段,這些自定義類型可以幫我們更高效的使用C語言。
相關(guān)文章
隊列的動態(tài)鏈?zhǔn)酱鎯崿F(xiàn)代碼分享
DynaLnkQueue.cpp - 動態(tài)鏈?zhǔn)疥犃?,即隊列的動態(tài)鏈?zhǔn)酱鎯崿F(xiàn)2014-02-02
C 語言實現(xiàn)一個簡單的 web 服務(wù)器的原理解析
這篇文章主要介紹了C 語言實現(xiàn)一個簡單的 web 服務(wù)器的原理解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
php5系列的apache遠(yuǎn)程執(zhí)行漏洞攻擊腳本
這篇文章主要介紹了php5系列的apache遠(yuǎn)程執(zhí)行漏洞攻擊腳本,需要的朋友可以參考下2014-06-06

