C語言之自定義型別
結構體型別的建立
- 首先我們要明確為什麼需要結構體?
有時候,我們要傳送多種資料的組合,這是我們就需要結構體。如果函式的引數多於四個就很容易出錯,效率也會降低,這是我們就可以用結構體壓縮引數個數。 - 怎樣建立一個結構體
這裡我們需要關鍵字struct,如下就是建立了一個結構體
struct tag
{
int a;
char c;
}stu;
- tag是結構體標籤
- stu是結構體的名字
- a和c為結構體成員
這裡tag可以省略,但是我們一般不要省略。這是因為省略之後不方便我們在相同型別的結構體。
例如:
struct
{
char name[20];
int age;
int sex;
}class;
struct
{
char name[20];
int age;
int sex;
}class1;
int main()
{
NULL;
}
這裡編譯器會把上邊兩個聲明當作不同的型別,而且如果我們在想建立一個相同成員的結構體class2,就必須的在main()函式前邊建立,這樣及其不方便。
struct tag
{
char name[20];
int age;
int sex;
}class;
int main()
{
struct tag class1;
}
有標籤時,如果還需要相同型別的結構體,在建立時就特別方便了。
- 結構體內部禁止定義自身變數
struct tag
{
char name[20];
int age;
int sex;
struct tag class2;
}class;
//這是錯誤的
struct tag
{
char name[20];
int age;
int sex;
struct tag *p;
}class;
//這裡我們可以定義自身型別的結構體變數,因為指標的大小在32位機器始終為4位元組。
- 結構體的訪問
struct tag
{
char name[20];
int age;
int sex;
}stu, *p;
stu.name;
結構體的初始化
我們先定義一個結構體
struct Point
{
int x;
int y;
}p1;
struct Point p2 = {1, 2}; //定義變數的同時賦初值
結構體的記憶體對齊
結構體的對齊原則
1. 第一個成員在與結構體變數偏移量為0的地址處。
2. 其他成員變數要對齊到對齊數的整數倍的地址處。對齊數=編譯器預設的一個對齊數與該成員大小的較小值。VS中預設為8,Linux中預設為4。(修改編譯器的對齊數(#pragma pack(1))這裡的數字如果大於編譯器的預設值,則對齊數預設為編譯器的對齊數)
3. 結構體的總大小為最大對齊數的整數陪。(每個成員除了第一個變數都有對齊數)
4. 如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍,結構體的整體大小就是所有最大對齊數的整數倍。(結構體的對齊數就是結構體的最大對齊數)
為什麼存在記憶體對齊
- 平臺原因
不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常。 - 效能原因
資料結構應該儘可能地在自然邊界上對齊。
位段
位段的宣告和結構體類似,有兩點不同:
1. 位段的成員必須是int、unsigned int或signed int
2. 位段的成員名後邊有一個冒號和一個數字。
- 位段的記憶體分配
- 位段的成員可以是int、unsigned int、signed int或者char(屬於整型家族)型別
- 位段的空間上是按照需要以上4個位元組(int)或者1位元組(char)的方式來開闢的。
- 位段涉及很多不確定因素,位段是不跨平臺的,注意可移植的程式應該用來避免使用位段。
struct A
{
int a:2;
int b:3;
int c:4;
};
//裡邊都是int型,佔8位元組記憶體
struct A
{
unsigned a : 19;
unsigned b : 11;
unsigned c : 4;
unsigned d : 29;
char index;
};
//這個佔16位元組,開始的時候我以為佔12位元組
特別注意:
C99規定int、unsigned int和bool可以作為位域型別,但編譯器幾乎都對此作了擴充套件, 允許其它型別的存在。
使用位域的主要目的是壓縮儲存,其大致規則為:
- 1)如果相鄰位域欄位的型別相同,且其位寬之和小於型別的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止;
- 2) 如果相鄰位域欄位的型別相同,但其位寬之和大於型別的sizeof大小,則後面的欄位將從新的儲存單元開始,其偏移量為其型別大小的整數倍;(經測試此條好像僅限於char型,整型可以跨域儲存)
- 3) 如果相鄰的位域欄位的型別不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式,Dev-C++採取壓縮方式;
- 4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮;
- 5) 整個結構體的總大小為最寬基本型別成員大小的整數倍。
和結構體相比,位段可以達到同樣的效果,但是可以很好的節省空間,但是有跨平臺問題存在。
列舉
enum
{
colour;
blue;
red;
yellow;
};
enum為關鍵字,裡邊的成員都為列舉常量,這些常量預設從0開始,依次遞增1。也可以賦初值。
enum
{
colour;
blue = 5; //這裡當blue被賦為5時,後邊的元素則在5的基礎上遞增1
red;
yellow;
};
列舉的好處:
- 提高程式碼的可讀性和維護性,便於除錯
- 與#define定義的識別符號相比,列舉有型別
聯合(共用體)
聯合也是一種自定義型別,這種型別定義的變數包含一些列成員,這些成員共用一塊空間。
union Un
{
char c;
int i;
}
在union中所有的資料成員共用一塊空間,同一時刻只能儲存其中一個數據成員,所有的資料成員具有相同的起始地址。
- 聯合大小的計算
1.聯合的大小至少是最大成員的大小
2.當最大成員大小不是最大對齊數的整數倍時,就要對齊到最大對齊數的整數倍。
判斷計算機大端小端
int checkSystem()
{
union
{
int i;
char ch;
}c;
c.i = 1;
return (c.ch ==1);
//如果是大端返回0,小端返回1
}