C語言:自定義類型詳解
一、結(jié)構(gòu)體
1.結(jié)構(gòu)體變量的定義及初始化
直接上代碼:
struct Point { int x; int y; }p1; //創(chuàng)建結(jié)構(gòu)體時(shí)順便創(chuàng)建變量,分號(hào)一定不能掉 struct Point p2; //單獨(dú)創(chuàng)建變量 struct Point p3 = { 1,2 }; //創(chuàng)建變量時(shí)順便賦值 struct Node { char str[20]; struct Point p; //結(jié)構(gòu)體嵌套 }n1 = { "abcd",{3,4} }; int main() { printf("%s\n", n1.str); //結(jié)構(gòu)體訪問時(shí),用.或者->,變量訪問用.,指針訪問用-> printf("%d\n", n1.p.x); printf("%d\n", n1.p.y); return 0; }
struct是創(chuàng)建結(jié)構(gòu)體的關(guān)鍵字,Point是結(jié)構(gòu)體的名字,p1為結(jié)構(gòu)體Point的一個(gè)變量,x,y稱為結(jié)構(gòu)體Point中的成員變量,變量的創(chuàng)建有兩種形式,第一,可以在創(chuàng)建結(jié)構(gòu)體時(shí)一起創(chuàng)建,第二,單獨(dú)創(chuàng)建,創(chuàng)建規(guī)則為類型+名稱,對(duì)于結(jié)構(gòu)體的賦值,可以在創(chuàng)建變量的時(shí)候順便賦值,可以先創(chuàng)建變量再單獨(dú)賦值。結(jié)構(gòu)體的訪問有兩種方式,當(dāng)使用變量進(jìn)行訪問時(shí),用.(點(diǎn)),再選擇該變量對(duì)應(yīng)的屬性;當(dāng)使用指針進(jìn)行訪問時(shí),用->,再選擇對(duì)應(yīng)的屬性即可。
2.結(jié)構(gòu)體內(nèi)存對(duì)齊
當(dāng)我們想去計(jì)算結(jié)構(gòu)體占內(nèi)存大小的時(shí)候,就需要知道結(jié)構(gòu)圖內(nèi)存對(duì)齊這一概念,我們先來看兩個(gè)例子:
struct A { char a; char b; int c; }; struct B { char a; int c; char b; }; int main(){ printf("%d\n", sizeof(struct A)); printf("%d\n", sizeof(struct B)); return 0; }
結(jié)果表明,A和B兩個(gè)結(jié)構(gòu)體所占內(nèi)存大小并不相等,但是其內(nèi)部成員變量是一樣的,只是順序不一樣。造成結(jié)果不同的原因就是因?yàn)閮?nèi)存對(duì)齊,我們來介紹結(jié)構(gòu)體內(nèi)存對(duì)齊的規(guī)則:
1.第一個(gè)成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處。 對(duì)齊數(shù) = 編譯器默認(rèn)的一個(gè)對(duì)齊數(shù) 與 該成員大小的較小值。在VS編譯器中,默認(rèn)對(duì)齊數(shù)是8。
結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(每個(gè)成員變量都有一個(gè)對(duì)齊數(shù))的整數(shù)倍。
如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整 體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。
3.為什么要內(nèi)存對(duì)齊呢?
從上面的結(jié)果我們大概就能猜到,為了節(jié)省空間。總的來說,主要原因有兩點(diǎn):
1.平臺(tái)原因:
某些硬件平臺(tái)只能在特定的地址處取地址,沒有內(nèi)存對(duì)齊,取值時(shí)可能會(huì)出錯(cuò)。
2.性能原因:
對(duì)于不對(duì)齊的情況,在讀取數(shù)據(jù)時(shí)可能要讀取兩次,以下圖為例:
在一個(gè)32位平臺(tái)上,在不對(duì)齊的情況下,如果想要讀取int的4個(gè)字節(jié),第一次會(huì)讀取到char的一個(gè)字節(jié),和int的后3個(gè)字節(jié)(小端),需要再讀取一次,才能將int的4個(gè)字節(jié)完全讀取出來;相比較而言,如果是在內(nèi)存對(duì)齊的情況下,只需要讀取一次就可以把int的4個(gè)直接讀取出來。
二、位段
結(jié)構(gòu)體還有實(shí)現(xiàn)位段的能力,問題來了,什么是位段呢?
1.什么是位段
位段的聲明和結(jié)構(gòu)體是類似的,但有兩點(diǎn)不同:
- 位段的成員必須是 int、unsigned int 或signed int 。
- 位段的成員名后邊有一個(gè)冒號(hào)和一個(gè)數(shù)字。
例如:
struct A { int _a:2; int _b:5; int _c:3; int _d:4; };
A就是一個(gè)位段類型,想要知道A的大小,同樣可以用sizeof來求。
2.位段的內(nèi)存分配
拿上面的位段A來說,會(huì)先在內(nèi)存中開辟一個(gè)4字節(jié)的空間,冒號(hào)后面的數(shù)字表示該成員變量所占內(nèi)存的大小,單位為bit,位段中的成員在內(nèi)存中從左向右分配, 當(dāng)一個(gè)結(jié)構(gòu)包含兩個(gè)位段,第二個(gè)位段成員比較大,無法容納于第一個(gè)位段剩余的位時(shí),是 舍棄剩余的位還是利用,這是不確定的??偟膩碚f,跟結(jié)構(gòu)相比,位段可以達(dá)到同樣的效果,但是可以很好的節(jié)省空間,但是有跨平臺(tái)的問題存在。光說不太好理解,我們來看下圖:
當(dāng)然,位段雖然比結(jié)構(gòu)體更節(jié)省內(nèi)存,但其存在跨平臺(tái)問題,需要謹(jǐn)慎使用。
三、枚舉
1.枚舉的定義
enum Day//星期 { Mon, //默認(rèn)情況下Mon值為0,后面的成員變量的值依次遞增 Tues, Wed, Thur, Fri, Sat, Sun };
當(dāng)然也可以在定義的時(shí)候賦值,例如:
enum Color//顏色 { RED=1, GREEN=2, BLUE=4 };
2.枚舉的優(yōu)點(diǎn)
我們知道,#define可以定義常量,那為什么要用枚舉呢?
枚舉的優(yōu)點(diǎn):
1.增加代碼的可讀性和可維護(hù)性
2.和#define定義的標(biāo)識(shí)符比較枚舉有類型檢查,更加嚴(yán)謹(jǐn)。
3.使用方便,一次可以定義多個(gè)常量
既然存在,就有其存在的道理,有些時(shí)候#define更方便,有時(shí)候枚舉更方便,我們要學(xué)會(huì)合理使用
四、聯(lián)合(共用體)
1.聯(lián)合類型的定義
union Un //聲明 { char c; int i; }; union Un un; //定義變量 printf("%d\n", sizeof(un)); //計(jì)算共用體的大小
2.聯(lián)合的特點(diǎn)
聯(lián)合的成員是共用同一塊內(nèi)存空間的,這樣一個(gè)聯(lián)合變量的大小,至少是最大成員的大小(因?yàn)?聯(lián)合至少得有能力保存最大的那個(gè)成員)。
union Un { int i; char c; }; union Un un; // 下面輸出的結(jié)果是一樣的嗎? printf("%d\n", &(un.i)); printf("%d\n", &(un.c)); //下面輸出的結(jié)果是什么? un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i);
從結(jié)果可以看出,i和c的地址是一樣的,因?yàn)樗麄児灿靡粔K空間,當(dāng)分別給i、c賦值時(shí),后賦值的c會(huì)覆蓋之前i的部分值。
3.聯(lián)合大小的計(jì)算
- 聯(lián)合的大小至少是最大成員的大小。
- 當(dāng)最大成員大小不是最大對(duì)齊數(shù)的整數(shù)倍的時(shí)候,就要對(duì)齊到最大對(duì)齊數(shù)的整數(shù)倍。
例如:
union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; //下面輸出的結(jié)果是什么? printf("%d\n", sizeof(union Un1)); //8,c占5個(gè)字節(jié),比i大,最大對(duì)齊數(shù)位4,需要為4的倍數(shù),所以為8 printf("%d\n", sizeof(union Un2)); //16,c占14個(gè)字節(jié),最大對(duì)齊數(shù)為4,所以為16
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Qt實(shí)現(xiàn)QLineEdit輸入前提示輸入范圍并用正則表達(dá)式限制輸入范圍
在日常開發(fā)過程中QLineEdit作為輸入框,有時(shí)要限制輸入的內(nèi)容,這篇文章主要給大家介紹了關(guān)于Qt實(shí)現(xiàn)QLineEdit輸入前提示輸入范圍并用正則表達(dá)式限制輸入范圍的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05詳解C++設(shè)計(jì)模式編程中策略模式的優(yōu)缺點(diǎn)及實(shí)現(xiàn)
這篇文章主要介紹了C++設(shè)計(jì)模式編程中策略模式的優(yōu)缺點(diǎn)及實(shí)現(xiàn),文中討論了策略模式中設(shè)計(jì)抽象接口的繼承和組合之間的區(qū)別,需要的朋友可以參考下2016-03-03C++中的頭文件與Extern(外部函數(shù)調(diào)用)方式
這篇文章主要介紹了C++中的頭文件與Extern(外部函數(shù)調(diào)用)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08C++實(shí)現(xiàn)LeetCode(90.子集合之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(90.子集合之二),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07c++11 chrono全面解析(最高可達(dá)納秒級(jí)別的精度)
chrono是c++ 11中的時(shí)間庫(kù),本文就來詳細(xì)的介紹一下chrono庫(kù)的具體使用,關(guān)鍵是理解里面時(shí)間段(Durations)、時(shí)間點(diǎn)(Time points)的概念,感興趣的可以了解一下2021-11-11C語言循環(huán)隊(duì)列的表示與實(shí)現(xiàn)實(shí)例詳解
這篇文章主要介紹了C語言循環(huán)隊(duì)列的表示與實(shí)現(xiàn),對(duì)于數(shù)據(jù)結(jié)構(gòu)與算法的研究很有幫助,需要的朋友可以參考下2014-07-07簡(jiǎn)單掌握桶排序算法及C++版的代碼實(shí)現(xiàn)
桶排序是將要排序的算法按桶分組排序之后再遍歷匯總的一種線性排序算法,下面就讓我們來通過小例子簡(jiǎn)單掌握桶排序算法及C++版的代碼實(shí)現(xiàn)^^2016-07-07