天天看點

40篇學完C語言——(第十二篇)【結構體簡介】

導讀:

結構體,怎麼了解?

你可以把它想象成一個桌面上的檔案夾,這個檔案夾裡面可以有各種各樣的檔案,當然也還可以再有檔案夾的存在,檔案夾裡面再放檔案……。如果你要修改其中一個檔案的内容,就是首先通過桌面上的那個檔案夾作為入口,然後一個一個的進入檔案夾去尋找你需要的檔案,找到之後就可以随你修改了。

long、unsigned int 、short、char(相當于各種檔案類型,比如 .txt、.c、.h)這些關鍵字是否很熟悉?這都是 C 語言定義好的資料類型,直接拿來用就行了。但是我想自定義一個别的類型的資料怎麼辦?

就靠struct了。結構體,顧名思義,就是将一個個資料類型構成一個資料類型以友善使用。

比如說一個 24 位的像素,有 R、G、B 三種顔色,每種顔色都用 8 bit 表示,如果使用一般的方法怎麼表示呢?看圖:

這樣表示當然沒有問題,也能解決你的需求,但是你在使用的時候會發現,通常他們使用場合一樣,隻是有的時候需要使用 Red 值,有的時候需要 Green,有的時候可能又要 Blue,但它們的共同點是都是用來表示一個像素的,那麼有沒有辦法把這些資料類型組合起來友善調用呢?當然有,就是今天的主角,struct。

我們先看看使用 struct 如何表示一個像素:

Pixel 中文表示像素,這樣就通過這個結構體将三個資料結合在一起了(用檔案夾裝在一起),并且這個新組合的資料類型就叫 Pixel,和 int、char 等類似。

那麼我們如何像使用 int、char 一樣定義一個代表像素的資料類型呢?

就是通過 struct + 結構體名 定義了。

這裡定義了兩個像素,每個像素下都有 Red、Green、Blue 這三個位元組資料,也就是說共有六個位元組的空間:

那麼對于這些資料如何使用呢?比如說要設定 Red=100,Green=120,Blue=210:

這樣就行了,是不是很簡單呢!如果你的 MDK 開啟了輸入補充功能,那麼寫這些代碼就更容易了:

可以看到你在敲完 Pixel1. 的圓點後,結構體的成員立馬出來了,這時候你就能自己選擇哪一個成員了,是不是很友善呢。不然一個結構體那麼多成員,怎麼記得住啊。

如果你用這個結構體資料類型定義了一個結構體指針,那麼就通過 -> 箭頭調用,相當友善的。

而且如果你想對整個結構體進行指派也是很友善的事情:

這樣一條語句就将三個成員變量的值進行了修改,和通過關鍵字定義的變量并沒什麼差別。

但還有一點,每次定義一個結構體變量都要敲 struct 關鍵字還是很麻煩的事情,是以這個時候可以使用 typedef 這個關鍵字了:

這樣聲明之後,每次要定義一個新的 Pixel 結構體,隻要使用 Pixel 就行了,而不必加入 struct 來聲明這是一個結構體。而為了讓自己知道這是一個自己定義的資料類型,一般會在名稱後面加 _t 或者 TypeDef 等。比如 GPIO 結構體:

并且結構體(檔案夾)裡面還可以套結構體(檔案夾),被套的結構體裡面也可能有結構體……。不僅能套結構體,指針、聯合體、枚舉、數組(各種檔案)也都是一樣的,而正常的 char、int 等更不用說了,完全按你的心意随意組合就是了(比如上面的結構體套了 uint16_t 和 兩個結構體)。

這些資料類型都可以通過結構體形成一個資料結構類型,是不是感覺特别友善啊。小項目可能結構體用的不多,但是大項目如果不用結構體,那麼操作資料類型是一件很麻煩的事情,是以一定要學會使用結構體。

當然了,套用的結構體多了,對運作效率還是有一些影響的,對一些性能要求比較高的地方可以不用結構體,或者通過一些方法提高效率,不然你套的深了,尋找其中的成員變量還是需要不少指令消耗的,這一點需要引起注意。另外,編譯器為了優化讀寫效率,可能會對資料類型進行填充:

這個存儲空間應該是 1+1+1+4 = 7,但是實際上是 8 (你可以通過 sizeof() 測試),就是因為 STM32 的處理字長為 4 個位元組,是最快的讀寫長度,是以對變量 Reserve 進行了 4 位元組對齊。它的存放位置如下:

當然這個是可以通過對齊方式進行修改的,但最好不要,因為這樣會降低讀寫效率,除非是那種定義好的通信協定,那沒辦法,隻能改了(這個坑千萬要注意)。

有的時候可能需要擷取結構體的偏移位址,此時就可用通過以下方法擷取:

結構體偏移量計算宏(非正常方法):

#define OFFSET(TYPE, MEMBER) ((unsigned long)(&(((TYPE *)0)->MEMBER)))

比如說要擷取 Green 在結構體 Pixel 的偏移位址,就可以通過上面的宏進行計算:

Offset 的結果就是 1,因為前一個 Red 共占用了一個位元組空間,是以它的偏移位址就是 1。

最後再看看結構體的初始化,就是對成員變量順序初始化:

繼續閱讀