摘要:解決方案三結構體變量的定義和初始化有了結構體類型,那要怎么樣來定義結構體變量和初始化變量呢例聲明類型的同時定義變量定義結構體變量初始化定義變量的同時賦初值。
目錄
首先我們來了解一下結構的基礎知識:
結構是一些值的集合,這些值稱為成員變量。結構的每個成員可以不同類型的變量。
如數(shù)組是一組相同類型的元素的集合,而結構體也是一些值的集合,結構體的每個成員可以是不同類型的。
struct tag{ member-list;}variable-list;//結構體關鍵字:struct//結構體的標簽:tag//結構體的類型:struct tag//結構的成員列表:member_list//結構體變量列表:variable_list
例:
#include //聲明一個結構體類型//聲明一個學生類型,是想通過學生類型來創(chuàng)建學生變量(對象)//描述學生:屬性 - 名字+性別+年齡+電話號碼struct Stu{ char name[20]; //名字 char sex[10]; //性別 int age; //年齡 char phone[12];//電話}; //記住這里要加分號struct Stu s3;//全局變量int main(){ //創(chuàng)建的結構體變量 struct Stu s1; struct Stu s2; return 0;}
在聲明結構的時候,可以不完全的聲明。
例:
//匿名結構體類型struct{ int a; char c;}sa;struct{ int a; char c;}* psa;//匿名結構體指針類型
?思考:在上面代碼的基礎上,下面的代碼合法嗎?
int main(){ psa = &sa; return 0;}
執(zhí)行結果:
?警告:編譯器會把上面的兩個聲明當成完全不同的兩個類型,所以是非法的。
結論:當兩個匿名結構體類型內部的內容一樣時,仍然是兩個不同結構體類型?
思考1:在結構中包含一個類型為該結構本身的成員是否可以呢?
//代碼1struct Node{ int data; struct Node next;};
思考2:可行嗎?如果可以,那sizeof(struct Node)是多少?
解答:不行。
假設代碼1中的方式可以執(zhí)行,那么在創(chuàng)建結構體的過程中,struct Node next由于結構體struct Node類型還沒創(chuàng)建完成,所以其類型的大小是未知的,而struct Node類型的是否能成功創(chuàng)建又依賴于struct Node next類型大小的確定性。
所以這兩者自相矛盾。因此上述方法不行!
//代碼2struct Node{ int data;//4 數(shù)據(jù)域 struct Node* next;//4/8 指針域};
思考3:這串代碼為什么可以成功呢?
解答:首先此處結構體自應用方式并不是直接利用結構體來創(chuàng)建變量,而是創(chuàng)建指向該結構體類型的指針。
我們知道,指針的大小跟其所指向的類型無關,僅跟平臺環(huán)境有關,32位平臺指針大小為4個字節(jié),64位平臺,指針大小為8個字節(jié)。
正因為指針大小的確定性,所以再自引用的時候結構體類型的整體大小也是可以確定的。
思考4:這樣寫代碼可行嗎?
//代碼3typedef struct{ int data; Node *next;}Node;
解答:不行
由于此時struct后面省略掉了Node,所以匿名重新命名結構體為Node,那么此時編譯器就會不認識Node。(就好比先有雞還是先有蛋)
因為結構體類型有重命名才能產生Node,而此時還未定義Node就在結構體內部使用了Node,所以會產生錯誤。
解決方案:
typedef struct Node{ int data; struct Node *next;}Node;
有了結構體類型,那要怎么樣來定義結構體變量和初始化變量呢?
例1:
struct Point{ int x; int y;}p1; //聲明類型的同時定義變量p1struct Point p2; //定義結構體變量p2//初始化:定義變量的同時賦初值。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"};//結構體嵌套初始化 printf("%c %lf %lf %d %s/n", s.name, s.p.weight, s.p.height, s.age, s.sex); return 0;}
關于結構體的基本使用到這里我們就差不多掌握了。
現(xiàn)在我們深入討論一個問題:我們都知道任何的數(shù)據(jù)類型都應有其對應的內存空間大小。如?char大小為1個字節(jié),int 為4個字節(jié),double 為8個字節(jié)等,如果無法確定大小,就無法在創(chuàng)建的時候知道該分配給該類型變量的內存空間是多少。
那么,結構體的大小是多少?又該如計算結構體的大小呢??
讓我們來研究一下熱門考點:結構體內存對齊
1. 第一個成員在與結構體變量偏移量為0的地址處。
2. 其他成員變量要對齊到某個數(shù)字(對齊數(shù))的整數(shù)倍的地址處。
對齊數(shù) = 編譯器默認的一個對齊數(shù)與該成員大小的較小值。?
- VS中默認的值為8
- Linux中的默認值為4
3. 結構體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。
4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結構體的整體大小就是所有最大對齊數(shù)(含嵌套結構體的對齊數(shù))的整數(shù)倍。(注:不是整體,是自身最大對齊數(shù))
練習1:
#include struct s1{ char c1; int i; char c2;};int main(){ printf("%d/n", sizeof(struct s1)); return 0;}
執(zhí)行結果:
分析:
練習2:
#includestruct s2{ char c1; char c2; int i;};int main(){ printf("%d/n", sizeof(struct s2)); return 0;}
執(zhí)行結果:
分析:
?練習3:
#includestruct s3{ double d; char c; int i;};int main(){ printf("%d/n", sizeof(struct s3)); return 0;}
執(zhí)行結果:
分析:
?練習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í)行結果:
分析:
1. 平臺原因(移植原因): 不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址 處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
2. 性能原因: 數(shù)據(jù)結構(尤其是棧)應該盡可能地在自然邊界上對齊。 原因在于,為了訪問未對齊的內存,處理器 需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
?總體來說: 結構體的內存對齊是拿空間來換取時間的做法。
當我們在設計結構體的時候,既要滿足對齊,又要節(jié)省空間時:
我們需要讓占用空間小的成員盡量集中在一起。
例:
struct s1{ char c1; int i; char c2;};struct s2{ char c1; char c2; int i;};
此時s1和s2類型的成員一模一樣,但是s1和s2所占用的空間大小是有區(qū)別的,前者大小為12個字節(jié),后者為8個字節(jié),顯然后者這種方式空間利用的效率更高。
之前我們見過了 #pragma 這個預處理指令,這里我們再次使用,可以改變我們的默認對齊數(shù)。
#include #pragma pack(8)//設置默認對齊數(shù)為8struct s1{ char c1; int i; char c2;};#pragma pack()//取消設置的默認對齊數(shù),還原為默認#pragma pack(1)//設置默認對齊數(shù)為1struct s2{ char c1; int i; char c2;};#pragma pack()//取消設置的默認對齊數(shù),還原為默認int main(){ printf("%d/n", sizeof(struct s1)); printf("%d/n", sizeof(struct s2)); return 0;}
執(zhí)行結果:
分析:
由于s1的值我們之前已經知道了大小為12,那么我們來分析一下s2的值為什么是6?
?結論:結構在對齊方式不合適的時候,我么可以自己更改默認對齊數(shù)。
寫一個宏,計算結構體中某變量相對于首地址的偏移,并給出說明
我們這邊首先要用到宏,即 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í)行結果:
直接舉例:
#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;}//結構體傳參void Print1(struct S tmp){ printf("%d %c %lf/n", tmp.a, tmp.c, tmp.d);}//結構體地址傳參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);//傳結構體 print2(&s);//傳結構體地址 return 0;}
結構體傳參有兩種方式:
- 傳遞結構體對象(傳值),對應的就是print1函數(shù)的方式
- 傳遞結構體地址(傳址),對應的就是print2函數(shù)的方式
思考:上面的print1和print2函數(shù)哪個好些 ?
答案:首選print2函數(shù)。
原因:
函數(shù)傳參的時候,參數(shù)是需要壓棧,會有時間和空間上的系統(tǒng)開銷。
如果傳遞一個結構體對象的時候,結構體過大,參數(shù)壓棧的的系統(tǒng)開銷比較大,所以會導致性能的下降。結論 : 結構體傳參的時候,要傳結構體的地址。
結構體講完就得講講結構體實現(xiàn) 位段?的能力。
位段的聲明和結構是類似的,有兩個不同:
- 位段的成員必須是 int、unsigned int 或signed int 。
- 位段的成員名后邊有一個冒號和一個數(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就是一個位段類型。 那位段A的大小是多少呢??
執(zhí)行結果:
1.位段的成員可以是int unsigned intsigned int或者是char(屬于整形家族)類型
2.位段的空間上是按照需要以4個字節(jié)([int)或者1個字節(jié)(char)的方式來開辟的。
3.位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段。
而這些不確定因素體現(xiàn)在:
(1)空間是否要被浪費?
(2)空間是從左向右使用還是從右向左使用?
比如說VS2019:先開辟1 / 4個字節(jié), 從右向左使用時,空間會被浪費
例:
#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í)行結果(前):
?執(zhí)行結果(后):
分析:
1)先開辟一個char類型大小的空間,也就是占用了1個字節(jié)。
2)將a的3個bit內容從右往左放入該字節(jié)中,此時還剩下5bit大小的空間
3)再將b的4個bit內容放到a的后面,此時還剩下1bit大小的空間
4)1bit空間內部不夠c中5bit的內容存放,于是重新開辟了一個字節(jié)空間,從右往左后此時剩下了3bit大小的空間
5)剩下的空間(3bit)不夠d的4bit內容存放,于是又重新開辟了一個字節(jié)空間,從右往左將d的4bit內容放進去
1. int 位段被當成有符號數(shù)還是無符號數(shù)是不確定的。
2. 位段中最大位的數(shù)目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題。
3. 位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。
4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是舍棄剩余的位 還是利用,這是不確定的。
總結:?跟結構相比,位段可以達到同樣的效果,但是可以很好的節(jié)省空間,但是有跨平臺的問題存在。
網絡傳輸協(xié)議包(計算機網絡)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/119016.html
目錄 一、枚舉 (一)枚舉類型的定義 (二)使用枚舉的原因? (三)枚舉的優(yōu)點? (四)枚舉的大小 (五)枚舉的使用 二、聯(lián)合(共用體) (一)聯(lián)合類型的定義 (二)聯(lián)合的特點 (三)面試題 (四)聯(lián)合大小的計算 一、枚舉 枚舉顧名思義就是:列舉?。? ?即把可能的取值一一列舉出來。 比如我們現(xiàn)實生活中: 一周當中從周一至周日的7天,可以一一列舉;性別有:男、女、保密,可以一一列舉;月份有...
摘要:結構體類型的特殊聲明在初階結構體中,我們已經將了結構體類型是如何進行聲明的,那么在這里,我們將講一些特殊的結構體聲明不完全的聲明。所以我們應該這樣寫通過指針來找到下一個同類型結構體的寫法,我們就稱之為結構體的自引用。 ...
摘要:故使用無具體類型,又稱通用類型,即可以接收任意類型的指針,但是無法進行指針運算解引用,整數(shù)等。求指針所占字節(jié)而不是解引用訪問權限大小。數(shù)組就是整個數(shù)組的大小,數(shù)組元素則是數(shù)組元素的大小,指針大小都為。 ...
摘要:我們常用的結構,就是小端模式,什么則為大端模式沒學我也不知道是個啥,但還是擺出來。 目錄 傳統(tǒng)藝能?過渡區(qū)?正片開始?共用體原理?字節(jié)順序?大小端存儲?共用體判斷...
目錄 ? ?一、數(shù)據(jù)類型介紹 二、類型的意義 三、類型的基本歸類 整型家族 浮點數(shù)家族 構造類型(自定義類型) 指針類型 空類型 四、整形在內存中的存儲 原碼、反碼、補碼 大小端字節(jié)序 為什么有大端和小端? 一道經典筆試題 ?一、數(shù)據(jù)類型介紹 數(shù)據(jù)從大的方向分為兩類: 內置類型自定義類型內置類型我們前面已經學習過,如下: char? ? ? ? ? ? //字符數(shù)據(jù)類型 short? ? ? ...
閱讀 3025·2021-11-22 12:06
閱讀 605·2021-09-03 10:29
閱讀 6559·2021-09-02 09:52
閱讀 2023·2019-08-30 15:52
閱讀 3419·2019-08-29 16:39
閱讀 1198·2019-08-29 15:35
閱讀 2069·2019-08-29 15:17
閱讀 1427·2019-08-29 11:17