【C語言】自定義類型(結(jié)構(gòu)體,位段)詳解-創(chuàng)新互聯(lián)

目錄 一、結(jié)構(gòu)體

?1、結(jié)構(gòu)體聲明

網(wǎng)站的建設(shè)創(chuàng)新互聯(lián)公司專注網(wǎng)站定制,經(jīng)驗豐富,不做模板,主營網(wǎng)站定制開發(fā).小程序定制開發(fā),H5頁面制作!給你煥然一新的設(shè)計體驗!已為生料攪拌車等企業(yè)提供專業(yè)服務(wù)。

?2、結(jié)構(gòu)體自引用

?3、結(jié)構(gòu)體變量的定義和初始化

?4、結(jié)構(gòu)體內(nèi)存對齊

?5、結(jié)構(gòu)體傳參

二、位段

?1、什么是位段

?2、位段內(nèi)存的分配問題

?3、位段跨平臺問題

一、結(jié)構(gòu)體

?在開發(fā)的過程中,我們難免要去描述一些復(fù)雜的對象,而想要描述這些對象,就不能使用之前學(xué)過的int,double等這些類型。這時候就可以用到結(jié)構(gòu)體了,結(jié)構(gòu)體是一些值的集合,這些值稱為成員變量。結(jié)構(gòu)的每個成員可以是不同類型的變量。

?1、結(jié)構(gòu)體聲明

下面是聲明一個學(xué)生類型,例如:

struct Stu
{
    char name[20];//名字
    int age;//年齡
    char sex[5];//性別
    char id[20];//學(xué)號
};

?還有一種是結(jié)構(gòu)體的特殊聲明,就是可以不完全的聲明。表示匿名結(jié)構(gòu)體,即沒有寫結(jié)構(gòu)體標(biāo)簽。就如下面這種形式:

//匿名結(jié)構(gòu)體類型
struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}a[20],*p;

?上述定義的兩個結(jié)構(gòu)體類型都是struct,沒有對應(yīng)的結(jié)構(gòu)體標(biāo)簽,所以是匿名得結(jié)構(gòu)體。?而匿名結(jié)構(gòu)體因為沒有結(jié)構(gòu)體標(biāo)簽,每個struct 匿名結(jié)構(gòu)體類型都是獨立的,聲明匿名結(jié)構(gòu)體類型后,因為匿名結(jié)構(gòu)體類型都是獨立的。它只能在聲明定義的時候在后面同時創(chuàng)建對應(yīng)的結(jié)構(gòu)體變量。

?2、結(jié)構(gòu)體自引用

?結(jié)構(gòu)體里面可以放相同或者不同類型的多個成員變量,那么結(jié)構(gòu)體里面的成員變量可以是當(dāng)前結(jié)構(gòu)體類型創(chuàng)建的成員變量嗎?答:是可以的。

?首先先看一看上面的代碼是否可以,答:這種寫法是錯誤的,因為這個結(jié)構(gòu)體類型里面的成員變量還沒描述完就直接使用自身結(jié)構(gòu)體類型創(chuàng)建個變量,這樣是行不通的。

正確的結(jié)構(gòu)體自引用方法是:

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

這樣寫就能夠明確結(jié)構(gòu)體指針類型字節(jié)大小的,并且還能用這個結(jié)構(gòu)體類型創(chuàng)建其他變量,例如創(chuàng)建一個s的變量,里面的結(jié)構(gòu)體指針成員變量可以存放&s ,這個時候就可以存放這個結(jié)構(gòu)體類型創(chuàng)建的另外的變量的地址,可以通過解引用訪問到其他結(jié)構(gòu)體變量。

?3、結(jié)構(gòu)體變量的定義和初始化

有了上面的結(jié)構(gòu)體類型,那如何定義變量,其實很簡單。

?定義完變量就該初始化變量,如下:

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

struct Book  //類型聲明
{
   char BookName[20];//書名
   char author[20];//作者
};
struct Book s = {"C語言程序設(shè)計","zhangsan" };//初始化

?用struct Node類型創(chuàng)建了變量n1 和n2, 對n1和n2結(jié)構(gòu)體變量嵌套進行初始化,對n1變量進行初始化時,當(dāng)初始化到p這個struct Book變量時,因為其自身也是一個結(jié)構(gòu)體變量,對結(jié)構(gòu)體變量初始化里面成員可能有多個需要再嵌套一個{ },此時應(yīng)該對該結(jié)構(gòu)體變量里面的成員變量先進行初始化,最后在為最后一個結(jié)構(gòu)體指針變量進行初始化為NULL,這就是結(jié)構(gòu)體嵌套初始化。

?4、結(jié)構(gòu)體內(nèi)存對齊

?在前面我們已經(jīng)掌握結(jié)構(gòu)體的使用,但是我們還不清楚結(jié)構(gòu)體大小的計算,想要計算結(jié)構(gòu)體的大小,首先我們先要明白結(jié)構(gòu)體內(nèi)存的對齊,才能夠準(zhǔn)確的計算結(jié)構(gòu)體的大小。

?重點:計算結(jié)構(gòu)體大小,首先我掌握結(jié)構(gòu)體的對齊規(guī)則:

?(1)第一個成員在與結(jié)構(gòu)體變量偏移量為0的地址處。

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

?對齊數(shù) = 編輯器默認(rèn)的一個對齊數(shù)與該成員大小的較小值。VS中默認(rèn)對齊數(shù)為8。

?(3)結(jié)構(gòu)體總大小為大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。

?(4)如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。

?c1為結(jié)構(gòu)體S1的第一個成員變量,所以對齊到偏移量0處,c1成員對齊數(shù)為1,并且占用1個字節(jié),所以偏移量0開始的第一個內(nèi)存單元就為c1的空間。

?整形 i 為結(jié)構(gòu)體的第二個成員變量,對齊到自身成員對齊數(shù)最小整數(shù)倍位置,偏移量為0處的內(nèi)存單元已經(jīng)存放了c1,所以需要往后到偏移量為4的位置,才是i成員對齊數(shù)4的倍數(shù),即從偏移處4開始往后存放4個字節(jié)。

??c2為結(jié)構(gòu)體的第三個成員變量,對齊數(shù)為1個字節(jié),當(dāng)前可用的偏移量從8開始,而8是對齊數(shù)1的倍數(shù),此時從偏移量為8的位置開始存放c2,c2占用一個字節(jié),所以偏移8處就位c2存放的地址。此時,所有的成員變量都分配好空間,是從偏移量0到8,占用了9個字節(jié),而結(jié)構(gòu)體總大小為所有成員變量大對齊數(shù)4的倍數(shù),9不是4的倍數(shù),所以需要浪費3個字節(jié),最終計算出的大小為12。

?結(jié)構(gòu)體S2的計算方法和S1相似,根據(jù)上圖可以容易的計算出,在此處我就略寫了。?

?為什么存在內(nèi)存對齊???(有兩個原因)

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

?(2)性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問。

? 總體來說,結(jié)構(gòu)體的內(nèi)存對齊是拿空間來換取時間的做法。所以我們在設(shè)計結(jié)構(gòu)體的時候,盡可能的讓占用空間小的成員集中在一起。

??5、結(jié)構(gòu)體傳參

struct S
{
    int data[1000];
    int num;
};
struct S s = { {1,2,3,4}, 1000 };
//結(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);  //傳結(jié)構(gòu)體
    print2(&s); //傳地址
    return 0;
}

?上述運用了兩種傳送形式,一種是直接傳結(jié)構(gòu)體,另一種是傳地址。這兩種方法我們選第二種傳地址的方法,因為函數(shù)傳參的時候,參數(shù)是需要壓棧,會有時間和空間上的系統(tǒng)開銷。 如果傳遞一個結(jié)構(gòu)體對象的時候,結(jié)構(gòu)體過大,參數(shù)壓棧的的系統(tǒng)開銷比較大,所以會導(dǎo)致性能的下降。

二、位段

?1、什么是位段

?位段的聲明和結(jié)構(gòu)是類似的,有兩個不同:

1.位段的成員必須是 int、unsigned int 或signed int 。

2.位段的成員名后邊有一個冒號和一個數(shù)字。

上述A就是一個位段類型。此時計算結(jié)構(gòu)體A的大小為8個字節(jié)。

因為一個int類型是4個字節(jié) = 32個bit,所以我們先開辟一個int類型的4個字節(jié)32存放它們,a 占2個bit,b占5個bit,c占10個bit,此時,已經(jīng)占用了17個bit,而d需要30個bit,這時放不下d,所以還需要再開辟一個int類型的4個字節(jié)存放d,所以結(jié)構(gòu)體A的大小為8個字節(jié)。

?2、位段內(nèi)存的分配問題

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

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

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

?接下來我們通過例子能夠更好地講解位段內(nèi)存的分配,并且還能清晰看出數(shù)據(jù)是怎么存放的。

下面我通過簡單的畫圖可以幫助大家更好地理解。?

???????

?3、位段跨平臺問題

1. int 位段被當(dāng)成有符號數(shù)還是無符號數(shù)是不確定的。

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

3. 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配標(biāo)準(zhǔn)尚未定義。

4. 當(dāng)一個結(jié)構(gòu)包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是 舍棄剩余的位還是利用,這是不確定的。

?總結(jié):跟結(jié)構(gòu)體相比,位段可以達到同樣的效果,但是可以很好的節(jié)省空間,但是有跨平臺的問題存在。

本文要是有不足的地方,歡迎大家在下面評論,我會在第一時間更正。

???????

老鐵們,記著點贊加關(guān)注哦!!!

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

網(wǎng)站題目:【C語言】自定義類型(結(jié)構(gòu)體,位段)詳解-創(chuàng)新互聯(lián)
轉(zhuǎn)載來源:http://muchs.cn/article14/ejgde.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、建站公司營銷型網(wǎng)站建設(shè)、軟件開發(fā)、App開發(fā)、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)