談到自定義類型,首先想到的是struct 即我們常用的結構體。首先說一說什么是結構。。。
結構:它是一些值的集合,這些值稱為成員變量。結構的每個成員可以是不同類型的變量
一、結構體聲明是怎樣的呢?
假如要定義一個結構體A
有兩種:
1.struct A { 2.struct {
char c; char c;
...(標量、數組、指針、其他結構體等) ...(標量、數組、指針、其他結構體等)
}(它的變量列表) ; }(它的變量列表) ;
第2種定義雖然寫起來更方便,但當添加結構體變量的時候就只可以用一次了。舉個例子:
編譯器會把上面兩個聲明當成完全不同的兩個類型。那樣是非法的
注意:結構體定義並不是定義一個變量,而是定義了一種數據類型,這種類型是自定義的,它可以和語言本身所自有的簡單數據類型一樣使用(如 int )。
結構體本身並不會被作為數據而開辟內存,真正作為數據而在內存中存儲的是這種結構體所定義的變量。
對結構體的訪問和賦值通常采用 “.”操作符,即結構體變量名 .成員變量名 如訪問A 中的a 就是 x.a
也可以是“->”符,即x->a
二、結構體自引用,如下:
結構體的嵌套使用:
三、對結構體變量定義、初始化、傳參:
1.在聲明結構體時,在最后花括號后面就可以直接定義該結構體的變量或指針;
2. 也可以用struct 結構體名 結構體變量名;進行定義。
在進行初始化的時候需要注意 雖然允許對該變量整體初始化,但不允許整體賦值的。
即如有一結構體struct{
int x;
int y;
};
struct A a1={x,y};是可以的, 而a1.x=10; 或 a1.y=10; 這樣賦值是錯誤的。
再一個就是在進行結構體參數傳遞時,我們應該是傳遞結構體變量的地址,因為函數傳參,參數是需要壓棧的,如果在傳遞一個結構體對象的時候,結構體過大,參數壓棧的系統開銷會很大,會導致性能下降。
接下來重點了 :結構體內存對齊
之所以內存對齊 原因有二 1.硬件要求;不是所有的硬件平台都能訪問任意地址上的任意數據;只能在某些地址處取特定類型的數據。
2.數據結構應該盡可能地在自然邊界上對齊,增加cpu的執行效率
至於怎么對齊,直接上例子:
對齊數:該變量所占大小與編譯器默認的一個值的較小值(VS下為8,linux下為4)。
結構體最大對齊數:結構體中所有變量的對齊數的最大值。(ps:有沒有注意到它是不可能超過編譯器默認的對齊數~)
對齊規則:1..每個成員變量相對第一個成員變量地址偏移量大小要為此時對齊數的整數倍。
2.整個結構體的大小為其最大對齊數的整數倍。
再來一個難一點的例子:
這時候還要加上一條對齊規則:
3.如果嵌套了結構體,嵌套的結構體對齊到自己的最大對齊數的整數倍處,整個結構體大小要是所有對齊數中的最大對齊數的整數倍
struct B { char e[2]; //對齊數為1 < 8 按2對齊;直接放 存至[0,2) short h; //對齊數為2 < 8 按2對齊;已對齊,存至[2,4) //咔! 先停住,去算A的最大對齊數 //不難看處結構體A最大對齊數為8=min{max{int,double,short},8} //整個結構體按8對齊,從[8 開始存放A struct A { int a; //對齊數4 < 8 按4對齊;對齊了,存至[8,12) double b; //對齊數8 = 8 按8對齊;不齊,擴至[16 開始存,存至[16,24) short c; //對齊數2 < 8,按2對齊;對齊了,存至[24,26) }p; }x; //對整體B,其大小要為最大對齊數8的倍數,所以最后大小為32 int main(){ printf("%d",sizeof( x)); return 0; }
注意可以通過#pragma pack (n) 改變編譯器默認對齊數,通過#pragma pack() 取消對其的修改。
比如對上面例子修改並在linux環境下運行:
1 #pragma pack (8) 2 struct A { 3 int a; 4 double b; 5 short c; 6 }p; 7 struct B { 8 char e[2]; 9 short h; 10 A p; 11 }x; 12 int main(){ 13 printf("%d",sizeof( x)); 14 return 0; 15 }答案為:20 和沒加#pragma pack (8)一樣
注意:#pragma pack的n值無論怎么修改都 只能改得比編譯器默認對齊數小,改大了是沒效果的,
同時,當n等於或超過所有數據成員長度的時候,這個n值的大小一樣不產生任何效果。(ps:理由就是在選對齊數的時候就選的跟默認對齊數比 較小的那一個)
位段:
位段聲明和結構體類似,只是
1.位段的成員必須是int\ unsigned int或signed int
2.位段的成員名后面有一個冒號和數字。
位段內存分配:
1. 位段的成員可以是 int 、unsigned int、 signed int 或者是 char (屬於整形家 族)類型
2. 位段的空間上是按照需要以4個字節( int )或者1個字節( char )的方式來開辟 的。
3. 位段涉及很多不確定因素,位段是不跨平台的,注重可移植的程序應該避免使用位 段
看一個例子:
-> _d在32位機器下 在最后剩下的15個比特位是存不下了的,這時候就會出現問題了。
所以位段雖然更好節省空間,但有幾個跨平台問題:
1. int位段被當成有符號數還是無符號數是不確定的。
2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫 成27,在16位機器會出問題。)
3. 位段中的成員在內存中從左向右分配,還是從右向左分配標准尚未定義。
4. 當一個結構包含多個個位段,一個位段成員比較大,無法容納於當前 剩余的位時,是舍棄剩余的位還是利用,這是不確定的。
枚舉和聯合體:
枚舉:一一列舉出來可能的取值。
優點:1. 增加代碼的可讀性和可維護性
2. 很#define定義的標識符比較枚舉有類型檢查,更加嚴謹。
3. 防止了命名污染(封裝)
4. 便於調試;
5. 使用方便,一次可以定義多個常量
定義:
(ps: 不再是分號 ,改為了逗號)
{ }中的內容是枚舉類型的可能取值,也叫枚常量 。這些可能取值都是有值的,默認從0 開始,一次遞增1,當然在定義的時候也可以賦初值。
注意問題:
聯合體(共用體):定義的變量包含一系列成員,這些成員公用一塊空間。
其定義與用法和結構體極為類似。
特點:聯合體的成員共用一塊內存的,一個聯合體變量的大小,至少是最大成員的大小。當最大成員大小不是最大對齊數的整數倍時,就要使其對齊到最大對齊數的整數倍。
對聯合體的典型的應用就是用來判定計算機的存儲方式。
1 #include <cstdio> 2 int checkSystem() 3 { 4 union check 5 { 6 int i; 7 char ch; 8 }c; 9 c.i=1; 10 return (c.ch==1); 11 } 12 int main() 13 { 14 checkSystem()==1 ? printf("小端!") : printf("大端!"); 15 return 0; 16 }

如果采用小端存儲,整型i低位就會放置在低位地址處(記小端有個口訣:小 小 小),當輸出ch就會輸出 1
如果是大端,此時ch就會輸出最高的一個字節也就是:0。
有錯望指出~