国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【C語言進(jìn)階】自定義類型(1)結(jié)構(gòu)體

lanffy / 598人閱讀

摘要:解決方案三結(jié)構(gòu)體變量的定義和初始化有了結(jié)構(gòu)體類型,那要怎么樣來定義結(jié)構(gòu)體變量和初始化變量呢例聲明類型的同時(shí)定義變量定義結(jié)構(gòu)體變量初始化定義變量的同時(shí)賦初值。

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

目錄

一、結(jié)構(gòu)體類型的聲明

結(jié)構(gòu)的聲明

特殊的聲明

二、結(jié)構(gòu)的自引用

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

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

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

結(jié)構(gòu)體的對齊規(guī)則

為什么存在內(nèi)存對齊?

修改默認(rèn)對齊數(shù)

百度筆試題

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

六、結(jié)構(gòu)體實(shí)現(xiàn)位段(位段的填充&可移植性)

什么是位段?

位段的內(nèi)存分配

位段的跨平臺問題

位段的應(yīng)用


一、結(jié)構(gòu)體類型的聲明

首先我們來了解一下結(jié)構(gòu)的基礎(chǔ)知識:

結(jié)構(gòu)是一些值的集合,這些值稱為成員變量。結(jié)構(gòu)的每個(gè)成員可以不同類型的變量。

如數(shù)組是一組相同類型的元素的集合,而結(jié)構(gòu)體也是一些值的集合,結(jié)構(gòu)體的每個(gè)成員可以是不同類型的。

結(jié)構(gòu)的聲明

struct tag{    member-list;}variable-list;//結(jié)構(gòu)體關(guān)鍵字:struct//結(jié)構(gòu)體的標(biāo)簽:tag//結(jié)構(gòu)體的類型:struct tag//結(jié)構(gòu)的成員列表:member_list//結(jié)構(gòu)體變量列表:variable_list

例:

#include //聲明一個(gè)結(jié)構(gòu)體類型//聲明一個(gè)學(xué)生類型,是想通過學(xué)生類型來創(chuàng)建學(xué)生變量(對象)//描述學(xué)生:屬性 - 名字+性別+年齡+電話號碼struct Stu{    char name[20]; //名字    char sex[10];  //性別    int age;       //年齡    char phone[12];//電話}; //記住這里要加分號struct Stu s3;//全局變量int main(){    //創(chuàng)建的結(jié)構(gòu)體變量    struct Stu s1;    struct Stu s2;    return 0;}

特殊的聲明

在聲明結(jié)構(gòu)的時(shí)候,可以不完全的聲明。

例:

//匿名結(jié)構(gòu)體類型struct{    int a;    char c;}sa;struct{    int a;    char c;}* psa;//匿名結(jié)構(gòu)體指針類型

?思考:在上面代碼的基礎(chǔ)上,下面的代碼合法嗎?

int main(){    psa = &sa;    return 0;}

執(zhí)行結(jié)果:

?警告:編譯器會把上面的兩個(gè)聲明當(dāng)成完全不同的兩個(gè)類型,所以是非法的。

結(jié)論:當(dāng)兩個(gè)匿名結(jié)構(gòu)體類型內(nèi)部的內(nèi)容一樣時(shí),仍然是兩個(gè)不同結(jié)構(gòu)體類型?

二、結(jié)構(gòu)的自引用

思考1:在結(jié)構(gòu)中包含一個(gè)類型為該結(jié)構(gòu)本身的成員是否可以呢?

//代碼1struct Node{    int data;    struct Node next;};

思考2:可行嗎?如果可以,那sizeof(struct Node)是多少?

解答:不行。

假設(shè)代碼1中的方式可以執(zhí)行,那么在創(chuàng)建結(jié)構(gòu)體的過程中,struct Node next由于結(jié)構(gòu)體struct Node類型還沒創(chuàng)建完成,所以其類型的大小是未知的,而struct Node類型的是否能成功創(chuàng)建又依賴于struct Node next類型大小的確定性。

所以這兩者自相矛盾。因此上述方法不行!


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

//代碼2struct Node{    int data;//4 數(shù)據(jù)域    struct Node* next;//4/8 指針域};

思考3:這串代碼為什么可以成功呢?

解答:首先此處結(jié)構(gòu)體自應(yīng)用方式并不是直接利用結(jié)構(gòu)體來創(chuàng)建變量,而是創(chuàng)建指向該結(jié)構(gòu)體類型的指針。

我們知道,指針的大小跟其所指向的類型無關(guān),僅跟平臺環(huán)境有關(guān),32位平臺指針大小為4個(gè)字節(jié),64位平臺,指針大小為8個(gè)字節(jié)。

正因?yàn)橹羔槾笮〉拇_定性,所以再自引用的時(shí)候結(jié)構(gòu)體類型的整體大小也是可以確定的。


思考4:這樣寫代碼可行嗎?

//代碼3typedef struct{    int data;    Node *next;}Node;

解答:不行

由于此時(shí)struct后面省略掉了Node,所以匿名重新命名結(jié)構(gòu)體為Node,那么此時(shí)編譯器就會不認(rèn)識Node。(就好比先有雞還是先有蛋)

因?yàn)榻Y(jié)構(gòu)體類型有重命名才能產(chǎn)生Node,而此時(shí)還未定義Node就在結(jié)構(gòu)體內(nèi)部使用了Node,所以會產(chǎn)生錯(cuò)誤。

解決方案:

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

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

有了結(jié)構(gòu)體類型,那要怎么樣來定義結(jié)構(gòu)體變量和初始化變量呢?

例1:

struct Point{    int x;    int y;}p1;                //聲明類型的同時(shí)定義變量p1struct Point p2;    //定義結(jié)構(gòu)體變量p2//初始化:定義變量的同時(shí)賦初值。struct Point p3 = {x, y};

例2:

struct S //類型聲明{    char name;//姓名    int age;//年齡    double d;//身高    char sex;//性別};int main(){    struct S s = {"c", 20, 182.5, "boy"};//初始化    return 0;}

?例3:

#include struct T{    double weight;//體重    double height;//身高}p;struct S //類型聲明{    char name;//姓名    struct T p;    short age;//年齡    char sex[5];//性別};int main(){    struct S s = {"A", {63.5, 182.5}, 20, "boy"};//結(jié)構(gòu)體嵌套初始化    printf("%c %lf %lf %d %s/n", s.name, s.p.weight, s.p.height, s.age, s.sex);    return 0;}

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

關(guān)于結(jié)構(gòu)體的基本使用到這里我們就差不多掌握了。

現(xiàn)在我們深入討論一個(gè)問題:我們都知道任何的數(shù)據(jù)類型都應(yīng)有其對應(yīng)的內(nèi)存空間大小如?char大小為1個(gè)字節(jié)int4個(gè)字節(jié)double8個(gè)字節(jié)等,如果無法確定大小,就無法在創(chuàng)建的時(shí)候知道該分配給該類型變量的內(nèi)存空間是多少。

那么,結(jié)構(gòu)體的大小是多少?又該如計(jì)算結(jié)構(gòu)體的大小呢??

讓我們來研究一下熱門考點(diǎn):結(jié)構(gòu)體內(nèi)存對齊

結(jié)構(gòu)體的對齊規(guī)則

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

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

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

  • VS中默認(rèn)的值為8
  • Linux中的默認(rèn)值為4

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

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

練習(xí)1:

#include	struct s1{	char c1;	int i;	char c2;};int main(){	printf("%d/n", sizeof(struct s1));	return 0;}

執(zhí)行結(jié)果:

分析:


練習(xí)2:

#includestruct s2{	char c1;	char c2;	int i;};int main(){	printf("%d/n", sizeof(struct s2));	return 0;}

執(zhí)行結(jié)果:

分析:


?練習(xí)3:

#includestruct s3{	double d;	char c;	int i;};int main(){	printf("%d/n", sizeof(struct s3));	return 0;}

執(zhí)行結(jié)果:

分析:


?練習(xí)4:

#includestruct s3{	double d;	char c;	int i;};struct s4{	char c1;	struct s3 s3;	double d;};int main(){	printf("%d/n", sizeof(struct s4));	return 0;}

?執(zhí)行結(jié)果:

分析:

為什么存在內(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í)間的做法。

當(dāng)我們在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,既要滿足對齊,又要節(jié)省空間時(shí):

我們需要讓占用空間小的成員盡量集中在一起。

例:

	struct s1{	char c1;	int i;	char c2;};struct s2{	char c1;	char c2;	int i;};

此時(shí)s1s2類型的成員一模一樣,但是s1s2所占用的空間大小是有區(qū)別的,前者大小為12個(gè)字節(jié),后者為8個(gè)字節(jié),顯然后者這種方式空間利用的效率更高。

修改默認(rèn)對齊數(shù)

之前我們見過了 #pragma 這個(gè)預(yù)處理指令,這里我們再次使用,可以改變我們的默認(rèn)對齊數(shù)。

#include #pragma pack(8)//設(shè)置默認(rèn)對齊數(shù)為8struct s1{	char c1;	int i;	char c2;};#pragma pack()//取消設(shè)置的默認(rèn)對齊數(shù),還原為默認(rèn)#pragma pack(1)//設(shè)置默認(rèn)對齊數(shù)為1struct s2{	char c1;	int i;	char c2;};#pragma pack()//取消設(shè)置的默認(rèn)對齊數(shù),還原為默認(rèn)int main(){	printf("%d/n", sizeof(struct s1));	printf("%d/n", sizeof(struct s2));	return 0;}

執(zhí)行結(jié)果:

分析:

由于s1的值我們之前已經(jīng)知道了大小為12,那么我們來分析一下s2的值為什么是6?

?結(jié)論:結(jié)構(gòu)在對齊方式不合適的時(shí)候,我么可以自己更改默認(rèn)對齊數(shù)。


百度筆試題

寫一個(gè)宏,計(jì)算結(jié)構(gòu)體中某變量相對于首地址的偏移,并給出說明

我們這邊首先要用到宏,即 offsetof

#include#includestruct s2{	char c1;	int i;	char c2;};int main(){	printf("%d/n", offsetof(struct s2, c1));	printf("%d/n", offsetof(struct s2, i));	printf("%d/n", offsetof(struct s2, c2));	return 0;}

執(zhí)行結(jié)果:

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

直接舉例:

#include struct S{	int a;	char c;    double d;};struct S s = { {1, 2, 3, 4}, 1000 };void Init(struct S tmp){	tmp.a = 100;    tmp.c = "w";    tmp.d = 3.14;}//結(jié)構(gòu)體傳參void Print1(struct S tmp){	printf("%d %c %lf/n", tmp.a, tmp.c, tmp.d);}//結(jié)構(gòu)體地址傳參void Print2(struct S* ps){	printf("%d %c %lf/n", ps->a, ps->c, ps->d);}int main(){    struct S s = {0};    Init(s);    Print1(s);//傳結(jié)構(gòu)體	print2(&s);//傳結(jié)構(gòu)體地址	return 0;}

結(jié)構(gòu)體傳參有兩種方式:

  1. 傳遞結(jié)構(gòu)體對象(傳值),對應(yīng)的就是print1函數(shù)的方式
  2. 傳遞結(jié)構(gòu)體地址(傳址),對應(yīng)的就是print2函數(shù)的方式

思考:上面的print1和print2函數(shù)哪個(gè)好些 ?

答案:首選print2函數(shù)。

原因:

函數(shù)傳參的時(shí)候,參數(shù)是需要壓棧,會有時(shí)間和空間上的系統(tǒng)開銷。
如果傳遞一個(gè)結(jié)構(gòu)體對象的時(shí)候,結(jié)構(gòu)體過大,參數(shù)壓棧的的系統(tǒng)開銷比較大,所以會導(dǎo)致性能的下降。

結(jié)論 : 結(jié)構(gòu)體傳參的時(shí)候,要傳結(jié)構(gòu)體的地址。

六、結(jié)構(gòu)體實(shí)現(xiàn)位段(位段的填充&可移植性)

結(jié)構(gòu)體講完就得講講結(jié)構(gòu)體實(shí)現(xiàn) 位段?的能力。

什么是位段?

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

  1. 位段的成員必須是 int、unsigned int 或signed int 。
  2. 位段的成員名后邊有一個(gè)冒號和一個(gè)數(shù)字。?

例:?

#includestruct A{	int _a : 2;	int _b : 5;	int _c : 10;	int _d : 30;};int main(){	printf("%d/n", sizeof(struct A));	return 0;}

A就是一個(gè)位段類型。 那位段A的大小是多少呢??

執(zhí)行結(jié)果:


位段的內(nèi)存分配

1.位段的成員可以是int unsigned intsigned int或者是char(屬于整形家族)類型
2.位段的空間上是按照需要以4個(gè)字節(jié)([int)或者1個(gè)字節(jié)(char)的方式來開辟的。
3.位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應(yīng)該避免使用位段。

而這些不確定因素體現(xiàn)在:
(1)空間是否要被浪費(fèi)?
(2)空間是從左向右使用還是從右向左使用?
比如說VS2019:先開辟1 / 4個(gè)字節(jié), 從右向左使用時(shí),空間會被浪費(fèi)

例:

#include		struct S{	char a : 3;	char b : 4;	char c : 5;	char d : 4;};int main(){	struct S s = { 0 };	s.a = 10;	s.b = 12;	s.c = 3;	s.d = 4;	return 0;}

思考:空間是如何開辟的?

執(zhí)行結(jié)果(前):

?執(zhí)行結(jié)果(后):

分析:

1)先開辟一個(gè)char類型大小的空間,也就是占用了1個(gè)字節(jié)。

2)將a的3個(gè)bit內(nèi)容從右往左放入該字節(jié)中,此時(shí)還剩下5bit大小的空間

3)再將b的4個(gè)bit內(nèi)容放到a的后面,此時(shí)還剩下1bit大小的空間

4)1bit空間內(nèi)部不夠c中5bit的內(nèi)容存放,于是重新開辟了一個(gè)字節(jié)空間,從右往左后此時(shí)剩下了3bit大小的空間

5)剩下的空間(3bit)不夠d的4bit內(nèi)容存放,于是又重新開辟了一個(gè)字節(jié)空間,從右往左將d的4bit內(nèi)容放進(jìn)去

位段的跨平臺問題

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

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

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

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

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

位段的應(yīng)用

網(wǎng)絡(luò)傳輸協(xié)議包(計(jì)算機(jī)網(wǎng)絡(luò))

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/119016.html

相關(guān)文章

  • C語言進(jìn)階定義類型(2)枚舉&聯(lián)合

    目錄 一、枚舉 (一)枚舉類型的定義 (二)使用枚舉的原因? (三)枚舉的優(yōu)點(diǎn)? (四)枚舉的大小 (五)枚舉的使用 二、聯(lián)合(共用體) (一)聯(lián)合類型的定義 (二)聯(lián)合的特點(diǎn) (三)面試題 (四)聯(lián)合大小的計(jì)算 一、枚舉 枚舉顧名思義就是:列舉?。? ?即把可能的取值一一列舉出來。 比如我們現(xiàn)實(shí)生活中: 一周當(dāng)中從周一至周日的7天,可以一一列舉;性別有:男、女、保密,可以一一列舉;月份有...

    leiyi 評論0 收藏0
  • C語言中還有這些類型,別再說你不知道了!手把手帶你解鎖C語言中的定義類型,讓你寫你所想。

    摘要:結(jié)構(gòu)體類型的特殊聲明在初階結(jié)構(gòu)體中,我們已經(jīng)將了結(jié)構(gòu)體類型是如何進(jìn)行聲明的,那么在這里,我們將講一些特殊的結(jié)構(gòu)體聲明不完全的聲明。所以我們應(yīng)該這樣寫通過指針來找到下一個(gè)同類型結(jié)構(gòu)體的寫法,我們就稱之為結(jié)構(gòu)體的自引用。 ...

    hizengzeng 評論0 收藏0
  • C語言進(jìn)階:指針進(jìn)階續(xù)

    摘要:故使用無具體類型,又稱通用類型,即可以接收任意類型的指針,但是無法進(jìn)行指針運(yùn)算解引用,整數(shù)等。求指針?biāo)甲止?jié)而不是解引用訪問權(quán)限大小。數(shù)組就是整個(gè)數(shù)組的大小,數(shù)組元素則是數(shù)組元素的大小,指針大小都為。 ...

    ingood 評論0 收藏0
  • 五分鐘腳踩大小端模式——C語言進(jìn)階

    摘要:我們常用的結(jié)構(gòu),就是小端模式,什么則為大端模式?jīng)]學(xué)我也不知道是個(gè)啥,但還是擺出來。 目錄 傳統(tǒng)藝能?過渡區(qū)?正片開始?共用體原理?字節(jié)順序?大小端存儲?共用體判斷...

    andong777 評論0 收藏0
  • C語言進(jìn)階】??數(shù)據(jù)類型&&整型在內(nèi)存中的存儲

    目錄 ? ?一、數(shù)據(jù)類型介紹 二、類型的意義 三、類型的基本歸類 整型家族 浮點(diǎn)數(shù)家族 構(gòu)造類型(自定義類型) 指針類型 空類型 四、整形在內(nèi)存中的存儲 原碼、反碼、補(bǔ)碼 大小端字節(jié)序 為什么有大端和小端? 一道經(jīng)典筆試題 ?一、數(shù)據(jù)類型介紹 數(shù)據(jù)從大的方向分為兩類: 內(nèi)置類型自定義類型內(nèi)置類型我們前面已經(jīng)學(xué)習(xí)過,如下: char? ? ? ? ? ? //字符數(shù)據(jù)類型 short? ? ? ...

    Xufc 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<