日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言 如何创建adt_C语言探索之旅 | 第二部分第六课:创建你自己的变量类型

發(fā)布時(shí)間:2025/3/20 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言 如何创建adt_C语言探索之旅 | 第二部分第六课:创建你自己的变量类型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

內(nèi)容簡(jiǎn)介


  • 前言

  • 定義一個(gè) struct

  • 結(jié)構(gòu)體的使用

  • 結(jié)構(gòu)體指針

  • union

  • enum

  • 總結(jié)

  • 第二部分第七課預(yù)告

  • 1. 前言


    上一課是 C語言探索之旅 | 第二部分第五課:預(yù)處理 ,應(yīng)該是比較輕松的。

    這一課將會(huì)非常令人激動(dòng)也很有意思,不過有些難度。

    眾所周知,C語言是面向過程的編程語言,與 Java,C++,等面向?qū)ο蟮木幊陶Z言有所不同。

    在面向?qū)ο蟮木幊陶Z言中,有(class)的概念。

    C語言是沒有類這種“類型”的,但是 C語言就不能“模擬”面向?qū)ο缶幊塘藛?#xff1f;

    不,只要你設(shè)計(jì)得好,C語言也可以模擬面向?qū)ο缶幊獭?/p>

    這一課我們要學(xué)習(xí)的 struct(結(jié)構(gòu)體)的知識(shí)就可以使你有能力用 C語言實(shí)現(xiàn)“面向?qū)ο蟆薄?/p>

    前面我們學(xué)習(xí)了指針,數(shù)組,字符串和預(yù)處理,掌握這些知識(shí)你的 C語言水平已經(jīng)還不錯(cuò)啦。但是我們豈能就此止步,必須 Bigger than bigger~

    除了使用 C語言已經(jīng)定義的變量類型,我們還可以做一些更厲害的事情:創(chuàng)建你自己的變量類型。

    我們可以將其稱為“自定義的變量類型”,我們來看三種:struct,union 和 enum。

    因?yàn)楫?dāng)你需要編寫比較復(fù)雜的程序時(shí),你會(huì)發(fā)現(xiàn)創(chuàng)建自定義的變量類型是很重要的。

    幸好,這學(xué)起來其實(shí)也不是特別難。但是大家需要專心學(xué)習(xí)這一課,因?yàn)閺南乱徽n開始,我們會(huì)一直用到 struct 了。

    2. 定義一個(gè) struct


    什么是 struct 呢?

    struct 是 structure(表示“結(jié)構(gòu)”)的縮寫,所以 struct 的專業(yè)術(shù)語是“結(jié)構(gòu)體”。

    定義:struct 就是一系列變量的集合,但是這些變量可以是不同類型的。

    這個(gè)定義是不是喚起了大家對(duì)我們的老朋友數(shù)組的懷念啊?數(shù)組里面的每個(gè)成員都必須是同一個(gè)類型的,相比之下 struct 更靈活。

    一般來說,我們習(xí)慣把 struct 定義在 .h 頭文件中,也就是和預(yù)處理命令以及函數(shù)原型那群“家伙”在一起。

    下面就給出一個(gè) struct 的例子:

    struct?你的struct的名字
    {
    ????char?variable1;
    ????short?variable2;
    ????int?otherVariable;
    ????double?numberDecimal;
    };

    可以看到:struct 的定義以關(guān)鍵字 struct 開始,后面接你自定義的 struct 的名稱(比如 Dog,Cat,Person,等)。

    一般來說,在我的代碼里,我的 struct 的命名也是遵照變量的命名規(guī)則,唯有一點(diǎn)不一樣,就是 struct 的名稱我會(huì)將首字母大寫,例如:SchoolName。

    但是我的普通變量一般都是首字母小寫,例如:studentNumber。

    這樣做只是個(gè)人習(xí)慣,便于在代碼里區(qū)分普通的變量和自定義的變量,之后會(huì)學(xué)到的 enum 和 union,我也是習(xí)慣將其名稱的首字母大寫。

    在 struct 的名字之后,我們需要寫上一對(duì)大括號(hào),在這對(duì)大括號(hào)里面寫入你的 struct 要包含的各種類型的變量。

    通常說來,struct 的大括號(hào)內(nèi)至少得定義兩個(gè)變量吧。如果只有一個(gè)變量,那定義這個(gè)結(jié)構(gòu)體也沒什么意義。

    注意:不要忘了,在大括號(hào)后面還要加上一個(gè)分號(hào)(;),
    因?yàn)楫吘惯@個(gè) struct 是一個(gè)變量,變量的定義最后都要加分號(hào)的。

    如你所見,創(chuàng)建一個(gè)自定義的變量也不復(fù)雜么。其實(shí)結(jié)構(gòu)體就是各種基本類型變量的集合,是一個(gè)“大雜燴”。

    當(dāng)然以后的課程中我們還會(huì)看到:結(jié)構(gòu)體的嵌套定義(結(jié)構(gòu)體里包含另一個(gè)結(jié)構(gòu)體)。

    結(jié)構(gòu)體的例子


    假設(shè)我們需要自定義一個(gè)結(jié)構(gòu)體,它儲(chǔ)存屏幕上的一個(gè)點(diǎn)的坐標(biāo)。

    下面就給出 2D(D 是英語 dimension 的首字母,表示維度)世界的坐標(biāo)系的大致印象:

    當(dāng)我們?cè)?2D 世界中做研究時(shí),我們有兩個(gè)軸:

    • 橫坐標(biāo)軸(從左到右,一般也稱為 x 軸)。

    • 縱坐標(biāo)軸(從下到上,一般也稱為 y 軸)。

    只要數(shù)學(xué)還沒有還給小學(xué)體育老師,應(yīng)該都知道 x 和 y 軸的。

    現(xiàn)在,你可以寫出一個(gè)名叫 Coordinate(表示“坐標(biāo)”)的 struct 的定義了嗎?我看好你!

    可以自己先寫,然后對(duì)一下我們給出的參考答案:

    struct?Coordinate
    {
    ????int?x;??//?橫坐標(biāo)
    ????int?y;??//?縱坐標(biāo)
    };

    很簡(jiǎn)單,不是嗎?我們的 Coordinate 這個(gè) struct 包含了兩個(gè)變量:x 和 y,都是 int 類型,分別表示橫坐標(biāo)值和縱坐標(biāo)值。

    當(dāng)然了,如果你愿意,也可以創(chuàng)建一個(gè)表示 3D(三維)空間的點(diǎn)的 struct,只需要在剛才的 Coordinate 這個(gè)結(jié)構(gòu)體的基礎(chǔ)上加上 z 軸。

    結(jié)構(gòu)體里面的數(shù)組


    結(jié)構(gòu)體里面也可以存放數(shù)組。

    例如,可以構(gòu)造一個(gè)名叫 Person(表示“人”)的結(jié)構(gòu)體,如下所示:

    struct?Person
    {
    ????char?firstName[100];??//?名
    ????char?lastName[100];???//?姓
    ????char?address[1000];???//?地址

    ????int?age;??//?年齡
    ????int?boy;??//?性別,布爾值?:?1?=?boy(表示“男孩”),?0?=?girl(表示“女孩”)
    };

    可以看到,這個(gè)結(jié)構(gòu)體變量包含五個(gè)基本的變量。

    • 前三個(gè)分別表示 名、姓和地址,是字符數(shù)組。

    • 第四個(gè)是年齡。

    • 第五個(gè)是性別,是一個(gè)“布爾值”(當(dāng)然,C語言本身沒有定義布爾類型(true 或 false),但是可以用數(shù)值來“表示”布爾值的真或假),boy 這個(gè) int 變量的值如果為 1,那就是表示男孩;如果為 0,那就是女孩。

    這個(gè)結(jié)構(gòu)體可以用于構(gòu)建一個(gè)通訊錄程序。當(dāng)然,你完全可以在這個(gè)結(jié)構(gòu)體里再添加其他變量,使其更完善。

    只要內(nèi)存夠,一般來說在一個(gè)結(jié)構(gòu)體里沒有變量的數(shù)目限制。

    3. 結(jié)構(gòu)體的使用


    現(xiàn)在,我們的結(jié)構(gòu)體已經(jīng)定義在 .h 頭文件里了,那么我們就可以在 include(“包含”)此頭文件的文件中使用這些結(jié)構(gòu)體了。

    以下展示如何創(chuàng)建一個(gè)類型為 Coordinate(我們之前已經(jīng)定義了這個(gè)結(jié)構(gòu)體,表示二維空間的坐標(biāo))的變量:

    #include?"coordinate.h"??//?假設(shè)包含結(jié)構(gòu)體定義的頭文件叫?coordinate.h

    int?main(int?argc,?char?*argv[]){
    ????struct?Coordinate?point;??//?創(chuàng)建一個(gè)?Coordinate?類型的變量,名字是?point

    ????return?0;
    }

    如上,我們創(chuàng)建了一個(gè) Coordinate 類型的變量,名字是 point(表示“點(diǎn)”)。

    這個(gè)變量自動(dòng)擁有兩個(gè)子變量:x 和 y,都是 int 類型,分別表示此二維坐標(biāo)的橫坐標(biāo)值和縱坐標(biāo)值。

    你也許要問:“創(chuàng)建結(jié)構(gòu)體變量開頭的那個(gè)關(guān)鍵字 struct 是必須的嗎?”

    是的,是必須的。

    struct 關(guān)鍵字使電腦能夠區(qū)分基礎(chǔ)變量類型(例如 int)和自定義變量類型(例如 Coordinate)。

    然而,每次加 struct 關(guān)鍵字也有點(diǎn)麻煩。所以聰(懶)明(惰)伶(成)俐(性)的 C語言開發(fā)者設(shè)計(jì)了 typedef 關(guān)鍵字。

    當(dāng)然了,人類的大多數(shù)發(fā)明都是為了“懶惰”的緣故,能提高效率誰不愿意啊?

    typedef 關(guān)鍵字


    typedef 是 C語言的一個(gè)關(guān)鍵字,是 type(表示“類型”)和 define(表示“定義”)的縮合,顧名思義是表示“類型定義”。

    聽到“類型定義”,好像很難理解。但其實(shí) typedef 的作用并沒有它的含義那么“高深莫測(cè)”。

    重新回到剛才定義 Coordinate 這個(gè)結(jié)構(gòu)體的 .h 頭文件中。我們來加一條由 typedef 開頭的命令,目的是為 Coordinate 結(jié)構(gòu)體創(chuàng)建一個(gè)別名。

    什么是別名(alias)呢?

    就比如有一個(gè)人,真實(shí)姓名叫王小明,別名可以是小明,明明,等,但都代表那個(gè)人。

    有點(diǎn)類似 C++ 語言的引用的機(jī)制。

    所以對(duì)別名的操作就是對(duì)原先對(duì)象的操作。

    比如小時(shí)候你上課不乖,老師點(diǎn)名的時(shí)候,點(diǎn)到你的小名或者你的真實(shí)名字,都是叫的你,你就得去罰站。

    我們就在 Coordinate 結(jié)構(gòu)體的定義之前加這句命令吧,一般習(xí)慣加在后面的,但是加在前面也可以:

    typedef?struct?Coordinate?Coordinate;

    struct?Coordinate
    {
    ????int?x;
    ????int?y;
    };

    可以看到,我們新加了一行命令:

    typedef?struct?Coordinate?Coordinate;

    為了更好地理解這句命令的作用,我們把它拆為三部分來看:

  • typedef:說明我們將要?jiǎng)?chuàng)建一個(gè)別名。

  • struct Coordinate:這是我們要為其創(chuàng)建別名的結(jié)構(gòu)體。

  • Coordinate:這就是要?jiǎng)?chuàng)建的別名。

  • 所以,上面這句命令的含義就是“從今以后,Coordinate 就相當(dāng)于 struct Coordinate 了 ”。

    這樣做以后,我們就可以不用每次在創(chuàng)建一個(gè)新的 Coordinate 結(jié)構(gòu)體的變量時(shí)都加上 struct 關(guān)鍵字了。

    所以,我們的 .c 文件中就可以改寫為:

    int?main(int?argc,?char?*argv[]){
    ????Coordinate?point;??//?因?yàn)橛昧?typedef,電腦就清楚地知道此處的?Coordinate?其實(shí)就是?struct?Coordinate

    ????return?0;
    }

    當(dāng)然,別名不一定要叫 Coordinate,也可以叫作 Coor,也許更不容易混淆。例如:

    typedef?struct?Coordinate?Coor;

    struct?Coordinate
    {
    ????int?x;
    ????int?y;
    };

    Coor?coor;??//?創(chuàng)建一個(gè)結(jié)構(gòu)體變量

    建議大家在平時(shí)定義了 struct 類型后,也加一句 typdedef 命令,這樣在代碼里就不用每次新建一個(gè)此類型的變量時(shí)都要在開頭寫 struct 關(guān)鍵字了。

    很多程序員都會(huì)這么做。
    因?yàn)橐粋€(gè)好的程序員是懂得如何“偷懶”的程序員,這和一個(gè)懶惰的程序員是有區(qū)別的。
    我們要使代碼"write less,do more"(用盡量少的代碼做更多的事)。

    當(dāng)然,上面的代碼塊可以簡(jiǎn)寫為:

    typedef?struct?struct的名字
    {
    ??//?struct?的內(nèi)容
    }?別名;

    所以上面 Coordinate 的代碼塊可以簡(jiǎn)寫為:

    typedef?struct?Coordinate
    {
    ????int?x;
    ????int?y;
    }?Coordinate;

    注意:之后我們的示例代碼,有時(shí)會(huì)出現(xiàn)例如

    Person?player1;

    這樣的形式,那就是假定我們之前已經(jīng)用了 typedef 了:

    typedef?struct?Person?Person;

    這樣就可以省略開頭的 struct 關(guān)鍵字,不需要再寫成:

    struct?Person?player1;

    修改 struct 的成員變量


    既然我們的 point 變量(是 Coordinate 類型的,希望大家還沒暈)已經(jīng)創(chuàng)建好了,那我們就可以修改它的成員的值了。

    我們?nèi)绾卧L問 point 的兩個(gè)成員 x 和 y 呢?如下所示:

    int?main(int?argc,?char?*argv[]){
    ????Coordinate?point;

    ????point.x?=?10;
    ????point.y?=?20;

    ????return?0;
    }

    這樣,我們就順利地修改了 point 的兩個(gè)成員的值,使其 x 坐標(biāo)為 10,y 坐標(biāo)為 20。

    因此我們的點(diǎn)就位于坐標(biāo)系的(10, 20)處了。

    所以,為了能訪問到結(jié)構(gòu)體的某個(gè)成員,我們可以這樣做:

    結(jié)構(gòu)體實(shí)例名稱.成員名

    中間的點(diǎn)(.)表示“從屬”關(guān)系。

    如果有面向?qū)ο缶幊袒A(chǔ)的朋友,就會(huì)覺得:這與“類和對(duì)象”也太像了吧。

    是的,其實(shí)我們可以用 struct 來“模擬”類。

    如果我們用之前創(chuàng)建的 Person 這個(gè)結(jié)構(gòu)體來舉例的話:

    int?main(int?argc,?char?*argv[]){
    ????Person?user;??//?user?表示“用戶”

    ????printf("你姓什么???");
    ????scanf("%s",?user.lastName);

    ????printf("你名叫什么???");
    ????scanf("%s",?user.firstName);

    ????printf("原來你的名字是?%s%s,失敬失敬\n",?user.lastName,?user.firstName);

    ????return?0;
    }

    運(yùn)行輸出:

    你姓什么?王
    你名叫什么?小明
    原來你的名字是?王小明,失敬失敬

    我們把 user.lastName 傳給 scanf,使得用戶輸入的值直接修改 user 的 lastName 成員;我們對(duì) user.firstName 也是如此。

    當(dāng)然我們也可以再添加對(duì) address,age,boy 的賦值。

    當(dāng)然了,你也許會(huì)說:“我不知道結(jié)構(gòu)體的使用,我用兩個(gè)單獨(dú)的字符串變量 lastName 和 firstName 不是也可以做到和上述程序相同的事么?”

    是的,但是用結(jié)構(gòu)體的好處就是我們可以創(chuàng)建此結(jié)構(gòu)體的變量,將很多相關(guān)聯(lián)的數(shù)據(jù)封裝在一起,成為一個(gè)整體,而不是零散地定義。

    比如定義了 Person 這個(gè)結(jié)構(gòu)體之后,凡是用 Person 來創(chuàng)建的變量,里面都自動(dòng)包含了 lastName,firstName,address,age 和 boy 這五個(gè)變量,非常方便。

    比如我們可以這樣創(chuàng)建:

    Person?player1,?player2;??//?之前已經(jīng)用?typedef(?typedef?struct?Person?Person;?)

    在 player1 和 player2 中都包含 lastName,firstName,address,age 和 boy 這五個(gè)變量。

    我們也可以更“偷懶”一些:創(chuàng)建結(jié)構(gòu)體數(shù)組。例如:

    Person?players[2];

    這樣,我們就可以很方便的訪問 players[1] 當(dāng)中的變量了,例如:

    players[1].lastName?=?"xiaoming";

    用結(jié)構(gòu)體數(shù)組的好處是可以方便地使用循環(huán),等等。

    自測(cè)小練習(xí)


    創(chuàng)建一個(gè)名叫 CoderHub(「程序員聯(lián)盟」公眾號(hào))的結(jié)構(gòu)體,在定義里放入你想創(chuàng)建的變量。然后創(chuàng)建此結(jié)構(gòu)體的一個(gè)數(shù)組,用循環(huán)的方式給變量賦值,再用循環(huán)的方式打印出其中變量的信息。

    結(jié)構(gòu)體的初始化

    之前的課程里,我們建議對(duì)于基本變量,數(shù)組和指針,最好在創(chuàng)建的時(shí)候?qū)ζ涑跏蓟?。結(jié)構(gòu)體也不例外。

    初始化有一個(gè)很大的好處,就是避免此變量里存放“任意數(shù)據(jù)”。

    事實(shí)上,一個(gè)變量在創(chuàng)建時(shí),如果沒有初始化,那么它會(huì)取當(dāng)時(shí)在內(nèi)存那個(gè)位置所存的值,所以這個(gè)值的隨機(jī)性是很大的。

    我們來回憶一下,不同變量的初始化應(yīng)該怎么做:

    • 基礎(chǔ)變量(int,double,char,等):初始化為 0。

    • 指針:初始化為 NULL。事實(shí)上,NULL 位于 stdlib.h 標(biāo)準(zhǔn)庫頭文件中,是用 #define 預(yù)處理命令定義的一個(gè)常量。它的值通常是 0。雖然是 0,但是有多種定義形式,例如:

    #define?NULL?0
    #define?NULL?0L
    #define?NULL?((void?*)?0)

    但是我們只要每次用 NULL 就好了,為了清楚表明這是指針變量,而不是一般變量。

    • 數(shù)組:將每一個(gè)成員變量初始化為 0。

    那么對(duì)于我們的“朋友” 結(jié)構(gòu)體,我們?cè)趺闯跏蓟?#xff1f;

    其實(shí)結(jié)構(gòu)體的初始化也很簡(jiǎn)單,與數(shù)組的初始化很類似。我們可以像下面這樣定義:

    Coordinate?point?=?{0,?0};

    這樣,我們就依照順序?qū)?point.x 和 point.y 都初始化為 0 了。

    對(duì)于像 Person 這樣的結(jié)構(gòu)體,里面的變量類型有 char 型數(shù)組和 int,那么我們可以將 char 型數(shù)組初始化為 ""(雙引號(hào)中間為空)。

    我們可以像這樣初始化一個(gè)字符串,在 C語言探索之旅 | 第二部分第四課:字符串 那一課忘記提了。不過,我想現(xiàn)在提還不算晚吧。

    所以我們就可以這樣來初始化我們的 Person 結(jié)構(gòu)體變量:

    Person?player?=?{"",?"",?"",?0,?0};

    然而,我們也可以這樣來初始化一個(gè)結(jié)構(gòu)體變量:創(chuàng)建一個(gè)函數(shù),比如叫 initializeStruct,可以為每一個(gè)傳遞給它的結(jié)構(gòu)體做初始化,這樣就方便很多,特別是當(dāng)結(jié)構(gòu)體中的變量很多時(shí)。

    之前指針那一章我們也已經(jīng)學(xué)了,如果我們對(duì)函數(shù)傳遞普通變量,那么因?yàn)?C語言的函數(shù)參數(shù)傳遞方式是值傳遞,所以它會(huì)對(duì)傳給它的函數(shù)參數(shù)做一份拷貝,這樣函數(shù)里面修改的其實(shí)是那一份拷貝,真正的實(shí)參并沒有被改變。

    為了讓實(shí)參實(shí)實(shí)在在被修改,我們需要用到指針,也就是傳遞此變量的地址。

    對(duì)于結(jié)構(gòu)體,也需要這樣。因此,接下來我們就來學(xué)習(xí)如何使用結(jié)構(gòu)體指針。開始難起來咯,準(zhǔn)備好了嗎?

    4. 結(jié)構(gòu)體指針


    結(jié)構(gòu)體指針的創(chuàng)建其實(shí)和普通的指針變量創(chuàng)建沒什么區(qū)別。例如:

    Coordinate?*point?=?NULL;

    上面的代碼就創(chuàng)建了一個(gè)叫做 point 的 Coordinate 結(jié)構(gòu)體指針變量(Coordinate 是我們上面定義的表示坐標(biāo)的一個(gè)結(jié)構(gòu)體)。

    我們?cè)賮硖嵝岩淮?#xff1a;

    一般推薦寫成:

    Coordinate?*point?=?NULL;?//?星號(hào)挨著指針變量名字

    而不推薦寫成:

    Coordinate*?point?=?NULL;??//?星號(hào)挨著結(jié)構(gòu)體名,這種寫法不好!

    在指針的創(chuàng)建中,我們推薦第一種寫法。

    因?yàn)橛玫诙N寫法,如果你在一行上創(chuàng)建好幾個(gè)指針變量時(shí),會(huì)容易忘記在第二個(gè)之后的變量前加 * 號(hào)。例如,容易寫成這樣:

    Coordinate*?point1?=?NULL,?point2?=?NULL;???//?編譯會(huì)出錯(cuò)

    但這樣編譯會(huì)出錯(cuò),因?yàn)?point2 其實(shí)是 Coordinate 結(jié)構(gòu)體變量,而不是 Coordinate 結(jié)構(gòu)體指針變量!

    所以我們建議這樣寫:

    Coordinate?*point1?=?NULL,?*point2?=?NULL;

    在以前的課程中,對(duì)于基礎(chǔ)類型的指針變量,我們也是這樣建議:

    int?*number1?=?NULL,?*number2?=?NULL;

    特別是 int 型的指針,還很不容易察覺到錯(cuò)誤,如果寫成:

    int*?number1?=?NULL,?number2?=?NULL;

    編譯器是不會(huì)報(bào)錯(cuò)的。因?yàn)?NULL 的值就是 0,可以賦給 number2 這個(gè) int 型變量(注意:上面的 number2 不是 int 指針)。

    回顧總是很好的(“傷心總是難免的…”)。

    結(jié)構(gòu)體作為函數(shù)參數(shù)


    這里,我們主要來學(xué)習(xí)如何將一個(gè)結(jié)構(gòu)體指針(為什么是傳結(jié)構(gòu)體指針而不是傳結(jié)構(gòu)體,可以看之前的解釋)傳給一個(gè)函數(shù)(作為參數(shù)),使得函數(shù)內(nèi)部可以真正修改此結(jié)構(gòu)體。

    我們來看一個(gè)實(shí)例:

    #include?

    typedef?struct?Coordinate
    {
    ????int?x;??//?橫坐標(biāo)值
    ????int?y;??//?縱坐標(biāo)值
    }?Coordinate;

    void?initializeCoordinate(Coordinate?*point);??//?函數(shù)原型

    int?main(int?argc,?char?*argv[])?{
    ????Coordinate?myPoint;

    ????initializeCoordinate(&myPoint);??//?函數(shù)的參數(shù)是?myPoint?變量的地址

    ????return?0;
    }

    //?用于初始化結(jié)構(gòu)體變量
    void?initializeCoordinate(Coordinate?*point)?{
    ????//?結(jié)構(gòu)體初始化的代碼
    }

    上面的 initializeCoordinate 函數(shù)體內(nèi),我們將放置初始化結(jié)構(gòu)體的成員變量的代碼。

    我們按順序來看一下這段代碼:

    • 首先,我們定義了一個(gè)結(jié)構(gòu)體,叫做 Coordinate,里面包含兩個(gè)變量,x 和 y。

    • 我們?cè)?main 函數(shù)中創(chuàng)建了Coordinate 結(jié)構(gòu)體的變量,名字叫 myPoint。

    • 我們將 myPoint 的地址傳遞給 initializeCoordinate 這個(gè)函數(shù)。

    • 接下來,我們就在 initializeCoordinate 函數(shù)中添加初始化 x 和 y 變量的代碼吧:

    void?initializeCoordinate(Coordinate?*point){
    ????*point.x?=?0;
    ????*point.y?=?0;
    }

    point 前面的 * 號(hào)是必不可少的噢。因?yàn)?#xff0c;傳進(jìn)函數(shù)的參數(shù)是一個(gè)結(jié)構(gòu)體指針,我們要取到此結(jié)構(gòu)體,就需要用到“解引用”符號(hào):星號(hào)(*)。

    但是,認(rèn)真的讀者看出上面這個(gè)函數(shù)中的錯(cuò)誤了嗎?

    我們的初衷是想要:先用 * 號(hào)解引用 point 這個(gè)結(jié)構(gòu)體指針,取到結(jié)構(gòu)體,然后再用 . 號(hào)取到其中的變量 x 和 y。但是如果按上面的寫法,其實(shí)效果相當(dāng)于如下:

    *(point.x)?=?0;
    *(point.y)?=?0;

    因?yàn)?. 號(hào)的優(yōu)先級(jí)是高于 * 號(hào)的。

    有興趣可以看一下 C語言運(yùn)算符的優(yōu)先級(jí),不過之前的課我們也說過了,記不清怎么辦呢?加括號(hào)就解決啦。

    上面的代碼編譯是通不過的,因?yàn)榻Y(jié)構(gòu)體指針 point 并沒有成員叫 x 和 y,而且,對(duì)于結(jié)構(gòu)體指針我們也不能用 . 號(hào)來取到什么值。

    因此,我們需要修改一下。改為如下就可以了:

    void?initializeCoordinate(Coordinate?*point)?{
    ????(*point).x?=?0;
    ????(*point).y?=?0;
    }

    這樣就對(duì)了。用括號(hào)去掉了運(yùn)算符優(yōu)先級(jí)的影響。

    但是,之前也說過:程序員是懂得偷懶的一群人。

    如果每次要取結(jié)構(gòu)體的成員變量都要這么麻煩,先用 * 號(hào),還要加括號(hào),再用 . 號(hào)。想想都要讓 Denis Ritchie(C語言的作者)老爺子醉了。他是決不允許這種事發(fā)生的,因此,他就定義了一個(gè)新的符號(hào): ->(一個(gè)箭頭。是的,就是這么“霸氣側(cè)漏”)。

    用法如下:

    point->x?=?0;

    就相當(dāng)于:

    (*point).x?=?0;

    是不是簡(jiǎn)便了很多?

    記住:這個(gè)符號(hào),只能用在指針上面。

    因此,我們的函數(shù)可以改寫為:

    void?initializeCoordinate(Coordinate?*point)?{
    ????point->x?=?0;
    ????point->y?=?0;
    }

    我們?cè)?main 函數(shù)里也可以這樣寫:

    int?main(int?argc,?char?*argv[]){
    ????Coordinate?myPoint;
    ????Coordinate?*myPointPointer?=?&myPoint;

    ????myPoint.x?=?10;??//?用結(jié)構(gòu)體的方式,修改?myPoint?中的?x?值
    ????myPointPointer->y?=?15;??//?用結(jié)構(gòu)體指針的方式,修改?myPoint?中的?y?值

    ????return?0;
    }

    結(jié)構(gòu)體是 C語言中一個(gè)非常好用且很重要的概念,希望大家好好掌握!

    當(dāng)然,還有不少知識(shí)細(xì)節(jié),就要大家自己去看 C語言的經(jīng)典教材了,例如《C程序設(shè)計(jì)語言》(不是譚浩強(qiáng)那本《C語言程序設(shè)計(jì)》!而是 C語言作者寫的經(jīng)典之作),《C和指針》,《C專家編程》,《C語言深度解剖》,《C陷阱和缺陷》,等等。

    5. union


    union 是“聯(lián)合”的意思,是 C語言的關(guān)鍵字,也有的書上翻譯為“共用體”。

    我們可以來寫一個(gè) union 的例子。

    union?CoderHub
    {
    ????char?character;
    ????int?memberNumber;
    ????double?rate;
    };

    乍看之下,和 struct 沒什么區(qū)別么。但是真的沒有區(qū)別嗎?

    假如我們用 C語言的 sizeof 關(guān)鍵字(size 表示“尺寸,大小”,of 表示“…的”)來測(cè)試此 union 的大小(大小指的是在內(nèi)存中所占的字節(jié)(byte)數(shù),一個(gè)字節(jié)相當(dāng)于 8 個(gè) bit(二進(jìn)制位)):

    #include?

    typedef?union?CoderHub
    {
    ????char?character;??//?大小是?1?個(gè)字節(jié)
    ????int?memberNumber;??//?大小是?4?個(gè)字節(jié)
    ????double?rate;??//?大小是?8?個(gè)字節(jié)
    }?CoderHub;

    int?main(int?argc,?char?*argv[]){
    ????CoderHub?coderHub;

    ????printf("此?union?的大小是?%lu?個(gè)字節(jié)\n",?sizeof(coderHub));

    ????return?0;
    }

    運(yùn)行程序,輸出:

    此?union?的大小是?8?個(gè)字節(jié)

    假如我們對(duì)結(jié)構(gòu)體也做一次測(cè)試,對(duì)比一下:

    #include?

    typedef?struct?CoderHub
    {
    ????char?character;??//?大小是?1?個(gè)字節(jié)
    ????int?memberNumber;??//?大小是?4?個(gè)字節(jié)
    ????double?rate;??//?大小是?8?個(gè)字節(jié)
    }?CoderHub;

    int?main(int?argc,?char?*argv[]){

    ????CoderHub?coderHub;

    ????printf("此?struct?的大小是?%lu?個(gè)字節(jié)\n",?sizeof(coderHub));

    ????return?0;
    }

    運(yùn)行程序,輸出:

    此?struct?的大小是?16?個(gè)字節(jié)

    為什么我們自定義的 union 的大小是 8 個(gè)字節(jié),而 struct 是 16 個(gè)字節(jié)呢?

    這就涉及到 union(共用體)和 struct(結(jié)構(gòu)體)的區(qū)別了。

    struct 的大小是其中所有變量大小的總和。

    但是你會(huì)說:“不對(duì)啊, 1 + 4 + 8 = 13,為什么 sizeof(coderHub) 的值為 16 呢?”

    好問題!這個(gè)有點(diǎn)復(fù)雜,涉及到內(nèi)存對(duì)齊的問題,我們以后再說。如果你一定要知道,那是因?yàn)閮?nèi)存對(duì)齊使得第一個(gè) char 變量對(duì)齊了第二個(gè) int 變量的空間,也變成了 4,如此一來:4 + 4 + 8 = 16。

    有興趣的讀者可以去參考《C語言深度解剖》的解釋。

    在嵌入式編程等內(nèi)存有限的環(huán)境下,需要考慮內(nèi)存對(duì)齊,以節(jié)省空間。

    union 的大小等于其中最大(sizeof() 得到的值最大)的那個(gè)變量的大小。所以我們就知道了,其實(shí) union 的儲(chǔ)存是這樣的:其中的每個(gè)變量在內(nèi)存中的起始地址是一樣的,所以 union 同一時(shí)刻只能存放其中一個(gè)變量,union 的大小等于其中最大的那個(gè)變量,以保證可以容納任意一個(gè)成員。

    union 適合用在很多相同類型的變量集,但是某一時(shí)刻只需用到其中一個(gè)的情況,比較節(jié)省空間。

    6. enum


    看完了 struct(結(jié)構(gòu)體)和 union(聯(lián)合),我們最后來學(xué)習(xí)很常用的一個(gè)自定義變量類型:enum。

    enum 是 enumeration(表示“枚舉”)的縮寫,也是一個(gè) C語言關(guān)鍵字。

    枚舉是一個(gè)比較特別的自定義變量類型。當(dāng)初我學(xué) C語言時(shí),一開始還真有點(diǎn)不理解。但用得好,卻非常實(shí)用。

    我們之前學(xué)了:結(jié)構(gòu)體里面包含了多個(gè)可以是不同類型的成員變量(一說“成員”就有點(diǎn)面向?qū)ο蟮母杏X :P)。

    但是 enum(枚舉)里面是一系列可選擇的值。也就是說每次只能取其中一個(gè)值,聽著和 union 有點(diǎn)類似啊。但是 enum 和 union 還是有區(qū)別的。

    我們來舉一個(gè)例子就知道區(qū)別了:

    typedef?enum?Shape?Shape;

    enum?Shape???//?shape?表示“身材、體型”
    {
    ????THIN,???//?thin?表示“瘦”
    ????MEDIUM,???//?medium?表示“中等”
    ????FAT???//?fat?表示“胖”
    };

    所以,我們定義了一個(gè)名叫 Shape 的 enum 變量。其中有三個(gè)值,分別是 THIN,MEDIUM 和 FAT(身材有瘦,中等和胖之分)。不一定要大寫,只是習(xí)慣。

    那我們?cè)趺磥韯?chuàng)建 enum 變量呢?如下:

    Shape?shape?=?MEDIUM;

    shape 這個(gè)變量,我們?cè)诔绦蚶镆部梢栽賹⑵湫薷臑?THIN 或者 FAT。

    將數(shù)值賦給 enum 的成員


    大家看到 enum 和 union 以及 struct 的區(qū)別了嗎?是的,enum 的定義里,每個(gè)成員沒有變量類型(int,char,double,之類)!

    很奇怪吧。想起來為什么 enum 的成員習(xí)慣用大寫了嗎?

    對(duì),就是因?yàn)?enum 的每個(gè)成員都不是變量,而是常量!但是 enum 的機(jī)制和常量定義以及 #define 還是有些區(qū)別:

    像上面的代碼:

    typedef?enum?Shape
    {
    ????THIN,
    ????MEDIUM,
    ????FAT
    }?Shape;

    編譯器會(huì)自動(dòng)為其中的每一個(gè)成員綁定一個(gè)常量值,我們寫程序測(cè)試一下:

    #include?

    typedef?enum?Shape?Shape;

    enum?Shape
    {
    ????THIN,
    ????MEDIUM,
    ????FAT
    };

    int?main(int?argc,?char?*argv[])?{
    ????Shape?shape?=?THIN;
    ????printf("THIN?=?%d\n",?shape);

    ????shape?=?MEDIUM;
    ????printf("MEDIUM?=?%d\n",?shape);

    ????shape?=?FAT;
    ????printf("FAT?=?%d\n",?shape);

    ????return?0;
    }

    運(yùn)行程序,輸出:

    THIN?=?0
    MEDIUM?=?1
    FAT?=?2

    看到了嗎?編譯器自動(dòng)給這三個(gè)成員賦值 0,1 和 2。如果沒有指定 enum 成員的值,那么它們的值是從 0 開始,依次加 1。

    我們也可以自己來定義 enum 成員的值,不一定要每次讓編譯器給我們自動(dòng)分配。

    我們可以這樣寫:

    typedef?enum?Shape
    {
    ????THIN?=?40,
    ????MEDIUM?=?60,
    ????FAT?=?90
    }?Shape;

    這樣,我們就自己給每個(gè)成員定義了值。

    我們也可以讓編譯器為我們自動(dòng)分配幾個(gè)值,再自己定義幾個(gè)值,例如:

    typedef?enum?Shape
    {
    ????THIN,
    ????MEDIUM,
    ????FAT?=?90
    }?Shape;

    上面,我們沒有為 THIN 和 MEDIUM 賦值,那么編譯器會(huì)將他們賦值為 0 和 1。

    而 FAT,因?yàn)槲覀円呀?jīng)指定了其值為 90,所以 FAT 就等于 90。

    enum 和 #define 的區(qū)別


    是不是覺得 enum 和用 #define 來定義的常量是有些類似呢?

    其實(shí),還是有些不同的:

    • #define 宏常量(或預(yù)處理常量)是在預(yù)處理階段進(jìn)行簡(jiǎn)單替換,枚舉常量則是在編譯的時(shí)候確定其值。

    • 一般在編譯器里,可以調(diào)試枚舉常量,但是不能調(diào)試宏常量。

    • 枚舉可以一次定義大量相關(guān)的常量,而 #define 宏一次只能定義一個(gè)。

    7. 總結(jié)


  • 結(jié)構(gòu)體(struct)是一種自定義的變量類型,完全由我們自由發(fā)揮,自己定制(走的是“高級(jí)定制”的路線啊),與 int,double 等基礎(chǔ)變量類型有所區(qū)別。結(jié)構(gòu)體的使用可以讓我們的 C語言程序更加靈活,可以做更多事。

  • 結(jié)構(gòu)體里包含成員變量,通常是基礎(chǔ)變量類型的變量,如 int,double 等變量,但也可以有指針變量,數(shù)組,甚至其他的結(jié)構(gòu)體變量。

  • 為了訪問到結(jié)構(gòu)體的成員變量,我們可以用普通的結(jié)構(gòu)體方式訪問:結(jié)構(gòu)體變量名稱.成員變量名(中間用一個(gè)“點(diǎn)”連接)。

  • 我們也可以用特別簡(jiǎn)便的結(jié)構(gòu)體指針的方式來訪問結(jié)構(gòu)體的成員變量:結(jié)構(gòu)體指針變量名->成員變量名(中間用一個(gè)“箭頭”連接)。

  • union(“共用體”,或“聯(lián)合”)和 struct 的最大不同就是:union 的大小是其中容量最大的那個(gè)成員變量的大小,而結(jié)構(gòu)體的大小是每一個(gè)成員變量的總和(還要考慮內(nèi)存對(duì)齊)。union 一次只能取其中一個(gè)變量。

  • enum(枚舉)一次只能取其中的一個(gè)成員的值,這一點(diǎn)和 union 有些類似。但是 enum 的成員都是常量,而不是變量。而且 enum 的成員如果沒有指定數(shù)值,編譯器會(huì)按照遞增順序?yàn)槊恳粋€(gè)變量賦值,從 0 開始。

  • 8. 第二部分第七課預(yù)告


    今天的課就到這里,一起加油吧!

    下一課:C語言探索之旅 | 第二部分第七課:文件讀寫


    作者:謝恩銘
    出處:公眾號(hào)「程序員聯(lián)盟」
    原文鏈接:https://www.jianshu.com/p/39b41aa5cca7
    轉(zhuǎn)載請(qǐng)注明出處,謝謝合作!轉(zhuǎn)載授權(quán)請(qǐng)加我微信 frogoscar

    喜歡本文的朋友,歡迎關(guān)注公眾號(hào)?程序員聯(lián)盟,收看更多精彩內(nèi)容

    點(diǎn)個(gè)[在看],是對(duì)我最大的支持!

    總結(jié)

    以上是生活随笔為你收集整理的c语言 如何创建adt_C语言探索之旅 | 第二部分第六课:创建你自己的变量类型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。