www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導(dǎo)讀]1、奇妙的"結(jié)構(gòu)體" ? ??今天講解這塊內(nèi)容,主要是受上篇文章結(jié)構(gòu)體內(nèi)部對齊的影響,來重新為大家介紹一下結(jié)構(gòu)體(如果小伙伴對結(jié)構(gòu)體的基礎(chǔ)知識還不具備的話得回頭看一下相關(guān)C語言的書籍). ????結(jié)構(gòu)體:字面上的意思就是有著層次結(jié)構(gòu)的一種數(shù)據(jù)形式,所謂的層次


1、奇妙的"結(jié)構(gòu)體"

    今天講解這塊內(nèi)容,主要是受上篇文章結(jié)構(gòu)體內(nèi)部對齊的影響,來重新為大家介紹一下結(jié)構(gòu)體(如果小伙伴對結(jié)構(gòu)體的基礎(chǔ)知識還不具備的話得回頭看一下相關(guān)C語言的書籍).

    結(jié)構(gòu)體:字面上的意思就是有著層次結(jié)構(gòu)的一種數(shù)據(jù)形式,所謂的層次結(jié)構(gòu)就是我們在結(jié)構(gòu)體中定義的各種成員了,再白話一點(diǎn):結(jié)構(gòu)體就是一個(gè)數(shù)據(jù)包,里面可以包含各種各樣的數(shù)據(jù)。對于這句話我們還要深入挖掘一下"各種各樣的數(shù)據(jù)”,對于我們編程而言-“一切皆是數(shù)據(jù)”。進(jìn)一步理解,結(jié)構(gòu)體里面可以包括整個(gè)程序中你想包含的東西。如果這樣的推敲沒錯(cuò)的話,那結(jié)構(gòu)體還真有點(diǎn)東西了,作者畫個(gè)圖供大家揣摩,結(jié)構(gòu)體的奇妙之處全在圖里面了!

2、結(jié)構(gòu)體"硬核"技巧

    對于結(jié)構(gòu)體的應(yīng)用太多了,今天這篇文章我主要為大家總結(jié)平時(shí)關(guān)于結(jié)構(gòu)體的一些獨(dú)特小技巧,對于結(jié)構(gòu)體更多優(yōu)秀的編程表現(xiàn),只能作者后續(xù)總結(jié)歸納以后分享給大家。好,下面進(jìn)入這些有用的技巧:

1)結(jié)構(gòu)體初始化有講究 

   我們大部分初學(xué)的小伙伴可能都不怎么會跟結(jié)構(gòu)體變量直接進(jìn)行初始化,(哈哈,可能很多小伙伴定了了變量根本就不會進(jìn)行初始化)不過還是要養(yǎng)成比較好的編程習(xí)慣,雖然現(xiàn)在大部分集成開發(fā)環(huán)境都會為大家把一些全局變量初始化為0,不過對于代碼的可移植性、可預(yù)知性考慮還是建議既然定義了就要給一個(gè)初始狀態(tài)。

    好了,先上代碼:

 1#include <stdio.h>
2//結(jié)構(gòu)體定義
3typedef struct __tag_Test
4{
5    int param1;
6    int param2;
7}stTest;
8//結(jié)構(gòu)體初始化方式1
9stTest sTest1 = {
10    .param1 = 1,
11    .param2 = 2
12};
13//結(jié)構(gòu)體初始化方式2
14stTest sTest2 = {
15  1,2
16};
17/*********************************************
18 * Fuction: main
19 * Author : (公眾號:最后一個(gè)bug)
20 ********************************************/

21int main(void) { 
22
23    printf("sTest1.param1 = %d\n",sTest1.param1);
24    printf("sTest1.param2 = %d\n",sTest1.param2);
25    printf("sTest2.param1 = %d\n",sTest2.param1);
26    printf("sTest2.param2 = %d\n",sTest2.param2);
27    return 0;
28}
    解析一下: 上面是一個(gè)非常簡單的代碼,其中第一種結(jié)構(gòu)體的初始化是linux源碼中非常常見的一種方式,這種初始化的方式編譯器必須要遵循ISO C99標(biāo)準(zhǔn),否則只能使用第二種比較常規(guī)的方式,不過現(xiàn)在大部分編譯器都支持該標(biāo)準(zhǔn)所以也被比較普遍的使用,其帶來的 好處有幾點(diǎn) :1)對于結(jié)構(gòu)體成員變量的初始化更加清晰,特別是當(dāng)結(jié)構(gòu)體特別大的時(shí)候;2)不用在乎初始化的順序,只需要名字和數(shù)據(jù)對應(yīng)上即可,非常的靈活。

2)給同個(gè)結(jié)構(gòu)體取多個(gè)名字 

    當(dāng)你進(jìn)行C編程時(shí)間久了以后,對于所屬性一致的變量等你都會用結(jié)構(gòu)體進(jìn)行封裝,那么可能出現(xiàn)同一個(gè)結(jié)構(gòu)體可能多個(gè)地方使用的情況,并且結(jié)構(gòu)體的作用會有所不同,就好像一個(gè)人他可能是一名學(xué)生,也有可能在外面兼職做一名服務(wù)員等等,如果這個(gè)人不換一身衣服或者做個(gè)標(biāo)記什么的可能有時(shí)候我們難以分辨,那么在C程序里面我們會怎樣為他們換個(gè)衣服呢?簡單代碼如下:

 1#include <stdio.h>
2
3 struct __tag_Man
4{

5    int Age;
6    int Height;
7};
8
9typedef struct __tag_Man stStudent;
10typedef struct __tag_Man stWaiter;
11
12stStudent sStudent = {
13  .Age    = 12,
14  .Height = 20,
15};
16
17stWaiter sWaiter = {
18  .Age    = 12,
19  .Height = 20,
20};
21
22int main(void) 
23
24    printf("sStudent.Age    = %d\n",sStudent.Age);
25    printf("sStudent.Height = %d\n",sStudent.Height);
26    printf("sWaiter.Age     = %d\n",sWaiter.Age);
27    printf("sWaiter.Height  = %d\n",sWaiter.Height);
28    printf("公眾號:最后一個(gè)bug");
29    return 0;
30}

    解析一下:上面的代碼確實(shí)挺簡單的,可能有些小伙伴一眼就可以看懂,不過這種使用方法時(shí)非常有價(jià)值的,特別是以后大家玩算法,比如說:節(jié)點(diǎn)都是一樣的,不過節(jié)點(diǎn)分為子節(jié)點(diǎn)和父節(jié)點(diǎn),對于一些處理函數(shù)傳入的參數(shù)雖然是一樣的,不過意義卻不同,我們就可以通過這樣的方式進(jìn)行處理,從而增加代碼可讀性。


3)0地址與結(jié)構(gòu)體的妙用  

    這一塊的內(nèi)容算是這篇文章的重點(diǎn)內(nèi)容,各位小伙伴們要做好筆記了,作者一直非常強(qiáng)調(diào)一點(diǎn)的是多讀讀大佬們的代碼,并且善于總結(jié)一些常用的小技巧供大家平時(shí)使用,這不我們今天就拿Linux的kernal中的兩個(gè)宏定義來分享幾個(gè)結(jié)構(gòu)體小技巧:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \
         const typeof( ((type *)0)->member ) *__mptr = (ptr); \
         (type *)( (char *)__mptr - offsetof(type,member) );})

    解析一下:

    1)第一個(gè)宏定義的功能是獲得一個(gè)結(jié)構(gòu)體成員距離結(jié)構(gòu)體首地址的偏移量,參數(shù)TYPE : 結(jié)構(gòu)體類型;參數(shù)MEMBER : 結(jié)構(gòu)體成員,其實(shí)這個(gè)算是比較簡單的,把0地址強(qiáng)制類型轉(zhuǎn)化為結(jié)構(gòu)體類型指針,然后通過結(jié)構(gòu)體指向成員即可獲得結(jié)構(gòu)體成員變量,然后通過&進(jìn)行取地址便獲得了結(jié)構(gòu)體成員地址,成員的偏移 = (結(jié)構(gòu)體成員地址 - 結(jié)構(gòu)體首地址);然而結(jié)構(gòu)體首地址為0,這樣成員的偏移 = 結(jié)構(gòu)體成員地址,應(yīng)該足夠清楚了吧。

    2)第二個(gè)宏定義的功能是通過結(jié)構(gòu)體成員變量獲得對應(yīng)的結(jié)構(gòu)體首地址(也就是結(jié)構(gòu)體地址),參數(shù)ptr :結(jié)構(gòu)體成員變量地址;參數(shù)type : 結(jié)構(gòu)體類型;參數(shù)member :結(jié)構(gòu)體成員,這個(gè)宏定義可能對于一些小伙伴而言在寫法上有一點(diǎn)點(diǎn)難度,不過其主要分兩部分,第一部分通過typeof獲得成員的類型并定義了一個(gè)const指針,定義為const的目的是不讓用戶對0地址的內(nèi)容進(jìn)行寫操作,對于大部分芯片對不合法區(qū)域進(jìn)行讀寫會引起異常。第二部分通過使用offsetof宏定義獲得結(jié)構(gòu)體成員相對結(jié)構(gòu)體首地址的偏移,這樣一相減便獲得了當(dāng)前結(jié)構(gòu)體成員所屬結(jié)構(gòu)體的地址,原理公式:(結(jié)構(gòu)體地址 = 結(jié)構(gòu)體成員地址 - 結(jié)構(gòu)體成員的偏移)。

    3)這里大體說一下注意事項(xiàng) : 在第二點(diǎn)我們談到了typeof關(guān)鍵字,該關(guān)鍵字是GUN C標(biāo)準(zhǔn)中擴(kuò)展的關(guān)鍵字,所以在使用該宏定義的時(shí)候需要注意一下,不然采用其他標(biāo)準(zhǔn)進(jìn)行編譯可能會報(bào)錯(cuò)。

4)最后幫助大家理解的小程序 

    作者要說的全在代碼里面了:

#include <stdio.h> 

/**********************************
 * Fuction : from Linux Kernal 
 **********************************/

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \
               const typeof( ((type *)0)->member ) *__mptr = (ptr); \
               (type *)( (char *)__mptr - offsetof(type,member) );})

/**********************************
 * Fuction : 結(jié)構(gòu)體定義區(qū)
 * Author  : (公眾號:最后一個(gè)bug)
 **********************************/

typedef struct _tag_Test{ 
    int Member1; 
    int Member2; 
    int Member3; 
}STest;

int main(void) { 
    int iTest = 0;
    STest stTest;

    printf("offsetof(STest , Member3) : %d\n",offsetof(STest , Member3));  
    printf("&stTest                   : 0X%X\n",&stTest);    
    printf("&(stTest.Member3)         : 0X%X\n",&(stTest.Member3));  
    printf("container of Member3      : 0X%X\n",container_of(&(stTest.Member3),STest,Member3));

    //這里主要是進(jìn)一步讓大家理解第二個(gè)宏
    //且右側(cè)必須加()
    iTest = ({int Val = 5; Val;});

    printf("iTest = %d\n",iTest);
    printf("歡迎關(guān)注公眾號:最后一個(gè)bug\n");
    return 1
}

   

    程序運(yùn)行的結(jié)果如下:

offsetof(STest , Member3) : 8
&stTest                   : 0X452469E0
&(stTest.Member3)         : 0X452469E8
container of Member3      : 0X452469E0
iTest = 5
歡迎關(guān)注公眾號:最后一個(gè)bug


3、最后小結(jié)

        今天的小知識就為大家分享到這來了,里面還有很多作者沒講得特別全面的小知識,大家看完文章以后記得都查閱一下不懂的知識.


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉