1. 程式人生 > >C語言之自定義型別

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

}