• C_C語言的enum、struct、union的使用詳解

    2016-08-29

    OK今天我們講解C語言的三個特殊結構的使用,他們都不是基本類型,但是他們讓C語言變得更加強大,更加易用,下面就讓我們一一分析:

    首先我們介紹enum---->枚舉:

    為了更加直觀的對枚舉有一個印象我們還是老規矩,看代碼:

    我們看到我們定義了一個枚舉類型,然后再在主函數中將枚舉的一個元素打印了出來,結果正如我們所料,既然我們已經直觀的感受了enum的定義
    以及其最基本的用法,那么下面我們就要說說他的語法規定了:
    首先,如果你想要定義一個枚舉就必須使用關鍵字enum,關鍵字enum就是聲明接下來我們定義的是一個枚舉器,然后緊跟著的就是這個枚舉器的名稱
    了,在名稱的后面是一對大括號,最后分號,在枚舉器的大括號內設定都有哪些枚舉,以及每個枚舉的值是多少(在本例中,我們枚舉器的名稱為bool
    ,共有兩個枚舉,分別為值為1得到true和值為0的false),那么下面我們來討論一個比較深入的問題,每個枚舉究竟是什么,是變量嗎?為了弄清
    這個問題,其實有經驗的讀者就會想到,我們看一看他的匯編是什么樣子?好,接下來就讓我們看一看他的匯編是什么樣子:

    我們可以看到匯編的第一行表明了c文件的名稱為"enu.c",這正是我們所寫的C文件。第七行表明我們定義了一個main函數,而第八行正是我們的這個
    main函數的入口。我們看第四行有一個 .string "%d " 我們發現這正是我們在printf函數的第一個參數所寫。最后也是我們要看的重點:第十六行
    有一個"$1"這正是我們定義的枚舉true的值,這個時候我們知道我們的定義直接顯示在了代碼段中(學過操作系統的讀者知道,一個程序就為一個
    進程,每個進程系統都會為他分配獨立的內存空間,而這個內存空間分為:代碼段,常量段,全局棧,局部棧,堆區。我們寫的代碼放在堆區,而
    我們定義的變量的值是放在棧區的)這就說明的他不是變量不能和const最類比,不過倒是和#define挺像......
    那么下面我們討論枚舉的值:
    首先枚舉的值類型是什么?我可以提前告訴大家是整型。那么是不是可以是其他類型呢?這我說了不算,我們看示例:

    顯然不行,記住枚舉的值僅能是整型。
    好下面我們說說兩個枚舉能不能使用同一個值?如果我們不給枚舉賦值,那么枚舉的值是什么?我們還是看代碼:

    顯然是可以的,我們可以講兩個枚舉賦同一個值。我們也可以看到如果我們不為枚舉賦值,那么他就會從零開始,依次遞增賦值。
    下面我們來看enum的一些高級用法:
    每個枚舉可不可以在使用時賦值?
    如果我們想使用枚舉類型定義變量來接收枚舉的值應該怎么做?好我們先看代碼再做解釋;

    從代碼中我們可以看到如果要定義一個枚舉類型的變量,我們可以使用enum 枚舉名 變量名 = 變量值;其中最前面的enum是不能缺少的,有讀者就會想了
    如果每次都這樣定義豈不是很麻煩?請各位繼續向下看,我們可以使用typedef來為我們的枚舉類型定義一個別名,那么就不會那么麻煩了,而且我們看到
    他們的結果完全相同。如果你還是嫌麻煩,那么輕向上看,枚舉weekday經過我們的重新定義,我們發現我們在定義枚舉是就可以為他起別名了,很方便吧!

    下面我來來介紹一下union--->聯合。由于現在計算機性能的提高我們一般不再使用(除非一些特定場合)所以我們不再過多講解,力求簡單給大家一個印象即可:

    好關于聯合我們僅出示上圖這一個代碼示例,接下來我們共同分析一下上述大碼;
    我們可以見到代碼的第14~17行我們定義了一個聯合,而且我們還給聯合取了一個別名叫做“IP”,我們來看聯合的定義,首先還是使用關鍵字union來聲明
    接下來我們要定義一個聯合。接著就是我們的聯合的名稱。緊接著一對大括號,括號內定義我們的要定義的內容。可以看到我們定義了一個char數組和一個整數。
    下面我們來看主函數的代碼,首先我們打印了char,int,IP的內存大小--->char占一個字節,int占四個字節,IP占四個字節。看到這我們就奇怪了,根據結果
    char數組應該占四個字節,int占四個字節在一起應該是八個字節,可為什么總共只占了四個字節呢?這就是聯合的特性了,聯合內的變量共享內存,也就是說
    聯合內部僅有一個內存塊,同一時刻僅能保存一份數據,而所有變量共享這一份數據。每當我們對其中的某個變量賦值時,其他所有的變量的數據都會變化而,
    下面打印的結果也證實這一點。當我們給char數組進行賦值時,我們看到int得到了同樣的也發生了變化,當我們對int進行賦值時char數組也發生了變化同時得
    到了同樣的數據。最后一行我們將char和int的地址(關于內存地址的問題我們稍后講解)打印出來。我們發現他們處于同一地址,這一就不奇怪之前的結果了。
    不知道大家有沒有注意到我們的數組和int的結果是相反的,這是由于我們的系統是大端系統。


    好接下來我們講解本文的最后一個主題------struct,結構體:

    我們首先討論一下我們為什么要使用結構體?請各位思考下面的問題,我們要保存一個我們自定義的類型(例如我們要定義一個學生類型:包含姓名,學號,性別
    ,成績,聯系方式,家庭住址)怎么辦?想一想有沒有什么好的辦法可以將一個“數據包”組合起來組成我們的自定義類型?就目前我們所學過的知識來講還不行,
    這就是我們要使用結構體的原因:我們要將幾個數據組合起來構成一個自定義類型。
    下面我們看結構體的定義,首先還是一樣,如果我們想要定義一個結構體必須要使用struct來聲明接下來我們要定義的是一個結構體,然后是結構體的名稱,接著
    一對大括號,在大括號內部定義各種屬性,但是他的每個屬性都是有獨立的存儲空間而不是象union那樣僅使用一份內存。我們讓每個屬性單獨保留一份數據。以供
    我們使用。最后 大家看代碼不知道各位有沒有看出什么問題?我們可以看到我為每個屬性的末尾添加了注釋,而注釋的內容就是屬性的大小,我們相加只有52個
    字節,但是我們使用sizeof函數獲得了56字節,其實這就是內存對齊。

    下面我們來看結構體的聲明,初始化,與賦值:
    下面的代碼展示了四種結構體的聲明有初始化,當我們每次聲明一個結構體變量時,都會為我們分配相應的存儲空間,那么我們要使用這個變量就要為結構體的、
    每個屬性賦值,而賦值可以分為初始化時按順序賦值,初始化時按屬性名賦值,以及使用變量屬性分別賦值,還有就是直接使用另一個結構體變量賦值。首先我們
    來看代碼的第25行,我們聲明了一個結構體變量stu1,然后我們在之后的大括號內,根據屬性的類型以及順序,依次為各屬性賦值。接下來我們看第32行,我們先
    思考當屬性過于復雜時,我們并不知道他們的順序我們應該如何初始化?我們自然就想到,是不是可以使用屬性名來進行初始化,那么本行就是范例。我們再看第
    33行,我們亦可以使用另外一個結構體變量來初始化我們的新變量,我們通過下面的打印結果就可以看出,新的變量完全復制了用來初始化的變量。最后我們再看
    第26行,他首先聲明了一個空的結構體變量,然后再使用.來引用各屬性進行依次賦值(注意看第27,28,31行我們通過復制函數來對char數組進行復制)。而最后的結果也是意料之中。

    下面我們就來看一下結構體的數組。我們知道任何一個基本類型都可以聲明為對應類型的數組,那么我們聲明了一個結構體類型后是否也可以聲明一個本類型的
    數組,答案是肯定的,當然可以,由于我們已經學習過數組,所以我們直接看代碼:

    根據代碼我們可以看到,他就是結構體的初始化賦值,與數組的賦值,訪問,初始化的聯合使用。我在此就不在做過多的解釋,還請各位能夠自行理解。
    在本主題的最后,我還想講結構體的最后一個用法,我們來思考以下場景,我們想使用類似于位移的功能使用一個字節的每一個位,但是我們還想單獨訪問他的每
    一個位我們應該如何實現?其實我們可以使用結構體就是可以的。好我們下面來看看這是如何實現的。首先它的定義與普通的結構體沒什么不同,只是它內部的屬
    性只能是unsigned int或unsigned char兩種類型,接下來就是變量的名稱,但接下來就是重點了:緊接著后面我們要跟一個冒號然后冒號的后面我們再跟一個數字
    這個數字代表的就是這個變量占用的位數(一位或數位)。好了下面我們就來看一下代碼示例:

    我們可以看到我們定義了Po1、Po2、Po3三個類型他們分別使用了unsigned char和unsigned int兩個類型,首先我們知道,一個char占用一個字節(八個位),而
    unsigned int 占用4個字節(32個位)。我們先看Po1使用了八個一位的unsigned char由于每個unsigned char占一個為一共八個所有共占用了一個字節,而Po2共
    使用了九個占一字節的unsigned char共占九位,但我們看結果Po2卻占用了兩個字節(16位)這也就是所謂的內存對齊。最后我們看Po3我們在其內部分別設置了三
    個占一字節的unsigned int、三個占兩字節的unsigned int、三個占三字節的unsigned int共占18位,但是我們使用的是int所以使用了4個字節。
    最后我們再看我們為屬性賦值,我們看到我們根據位數的大小最多可以賦值一個同等位數的二進制數,如果賦值的這個十進制數大于最多可以使用的二進制數就會溢
    出,造成錯誤的結果,我們根據最后打印的結果也可以大致推算出來....
    好了我們今天就講解這些了,再見。


    天堂网