国产无码免费,人妻口爆,国产V在线,99中文精品7,国产成人无码AA精品一,制度丝袜诱惑av,久久99免费麻辣视频,蜜臀久久99精品久久久久久酒店
        訂閱
        糾錯
        加入自媒體

        C指針:8 個關于指針的用法

        2021-03-05 09:34
        道哥分享
        關注

        3. 傳遞函數指針

        從上篇文章中我們知道,函數名本身就代表一個地址,在這個地址中存儲著函數體中定義的一連串指令碼,只要給這個地址后面加上一個調用符(小括號),就進入這個函數中執行。在實際程序中,函數名常常作為函數參數來進行傳遞。

        熟悉C++的小伙伴都知道,在標準庫中對容器類型的數據進行各種算法操作時,可以傳入用戶自己的提供的算法函數(如果不傳入函數,標準庫就使用默認的)。

        下面是一個示例代碼,對一個 int 行的數組進行排序,排序函數 demo3_handle_data 的最后一個參數是一個函數指針,因此需要傳入一個具體的排序算法函數。示例中有 2 個候選函數可以使用:

        降序排列: demo3_algorithm_decend;升序排列: demo3_algorithm_ascend;typedef int BOOL;#define FALSE 0#define TRUE  1
        BOOL demo3_algorithm_decend(int a, int b){    return a > b;}
        BOOL demo3_algorithm_ascend(int a, int b){    return a < b;}
        typedef BOOL (*Func)(int, int);void demo3_handle_data(int *data, int size, Func pf){    for (int i = 0; i < size - 1; ++i)    {        for (int j = 0; j < size - 1 - i; ++j)        {            // 調用傳入的排序函數            if (pf(data[j], data[j+1]))            {                int tmp = data[j];                data[j] = data[j + 1];                data[j + 1] = tmp;            }        }    }}
        void demo3(){    int a[5] = {5, 1, 9, 2, 6};    int size = sizeof(a)/sizeof(int);    // 調用排序函數,需要傳遞排序算法函數    //demo3_handle_data(a, size, demo3_algorithm_decend); // 降序排列    demo3_handle_data(a, size, demo3_algorithm_ascend);   // 升序排列    for (int i = 0; i < size; ++i)        printf("%d ", a[i]);    printf("");}這個就不用畫圖了,函數指針 pf 就指向了傳入的那個函數地址,在排序的時候直接調用就可以了。
        4. 指向結構體的指針

        在嵌入式開發中,指向結構體的指針使用特別廣泛,這里以智能家居中的一條控制指令來舉例。在一個智能家居系統中,存在各種各樣的設備(插座、電燈、電動窗簾等),每個設備的控制指令都是不一樣的,因此可以在每個設備的控制指令結構體中的最前面,放置所有指令都需要的、通用的成員變量,這些變量可以稱為指令頭(指令頭中包含一個代表命令類型的枚舉變量)。

        當處理一條控制指令時,先用一個通用命令(指令頭)的指針來接收指令,然后根據命令類型枚舉變量來區分,把控制指令強制轉換成具體的那個設備的數據結構,這樣就可以獲取到控制指令中特定的控制數據了。

        本質上,與 Java/C++ 中的接口、基類的概念類似。

        // 指令類型枚舉typedef enum _CMD_TYPE_ {    CMD_TYPE_CONTROL_SWITCH = 1,    CMD_TYPE_CONTROL_LAMP,} CMD_TYPE;
        // 通用的指令數據結構(指令頭)typedef struct _CmdBase_ {    CMD_TYPE cmdType; // 指令類型    int deviceId;     // 設備 Id} CmdBase;
        typedef struct _CmdControlSwitch_ {    // 前 2 個參數是指令頭    CMD_TYPE cmdType;       int deviceId;        // 下面都有這個指令私有的數據    int slot;  // 排插上的哪個插口    int state; // 0:斷開, 1:接通} CmdControlSwitch;
        typedef struct _CmdControlLamp_ {    // 前 2 個參數是指令頭    CMD_TYPE cmdType;    int deviceId;        // 下面都有這個指令私有的數據    int color;      // 顏色    int brightness; // 亮度} CmdControlLamp;
        // 參數是指令頭指針void demo4_control_device(CmdBase *pcmd){    // 根據指令頭中的命令類型,把指令強制轉換成具體設備的指令    if (CMD_TYPE_CONTROL_SWITCH == pcmd->cmdType)    {        // 類型強制轉換        CmdControlSwitch *cmd = pcmd;        printf("control switch. slot = %d, state = %d ", cmd->slot, cmd->state);    }    else if (CMD_TYPE_CONTROL_LAMP == pcmd->cmdType)    {        // 類型強制轉換        CmdControlLamp * cmd = pcmd;        printf("control lamp.   color = 0x%x, brightness = %d ", cmd->color, cmd->brightness);    }}
        void demo4(){    // 指令1:控制一個開關    CmdControlSwitch cmd1 = {CMD_TYPE_CONTROL_SWITCH, 1, 3, 0};    demo4_control_device(&cmd1);
           // 指令2:控制一個燈泡    CmdControlLamp cmd2 = {CMD_TYPE_CONTROL_LAMP, 2, 0x112233, 90};    demo4_control_device(&cmd2);}
        5. 函數指針數組

        這個示例在上篇文章中演示過,為了完整性,這里再貼一下。

        int add(int a, int b) { return a + b; }int sub(int a, int b) { return a - b; }int mul(int a, int b) { return a * b; }int divide(int a, int b) { return a / b; }
        void demo5(){    int a = 4, b = 2;    int (*p[4])(int, int);    p[0] = add;    p[1] = sub;    p[2] = mul;    p[3] = divide;    printf("%d + %d = %d ", a, b, p[0](a, b));    printf("%d - %d = %d ", a, b, p[1](a, b));    printf("%d * %d = %d ", a, b, p[2](a, b));    printf("%d / %d = %d ", a, b, p[3](a, b));}
        6. 在結構體中使用柔性數組

        先不解釋概念,我們先來看一個代碼示例:

        // 一個結構體,成員變量 data 是指針typedef struct _ArraryMemberStruct_NotGood_ {    int num;    char *data;} ArraryMemberStruct_NotGood;
        void demo6_not_good(){    // 打印結構體的內存大小    int size = sizeof(ArraryMemberStruct_NotGood);    printf("size = %d ", size);
           // 分配一個結構體指針    ArraryMemberStruct_NotGood *ams = (ArraryMemberStruct_NotGood *)malloc(size);    ams->num = 1;
           // 為結構體中的 data 指針分配空間    ams->data = (char *)malloc(1024);    strcpy(ams->data, "hello");    printf("ams->data = %s ", ams->data);
           // 打印結構體指針、成員變量的地址    printf("ams = 0x%x ", ams);    printf("ams->num  = 0x%x ", &ams->num);    printf("ams->data = 0x%x ", ams->data);
           // 釋放空間    free(ams->data);    free(ams);}

        在我的電腦上,打印結果如下:

        可以看到:該結構體一共有 8 個字節(int 型占 4 個字節,指針型占 4 個字節)。

        結構體中的 data 成員是一個指針變量,需要單獨為它申請一塊空間才可以使用。而且在結構體使用之后,需要先釋放 data,然后釋放結構體指針 ams,順序不能錯。這樣使用起來,是不是有點麻煩?

        于是,C99 標準就定義了一個語法:flexible array member(柔性數組),直接上代碼(下面的代碼如果編譯時遇到警告,請檢查下編譯器對這個語法的支持):

        // 一個結構體,成員變量是未指明大小的數組typedef struct _ArraryMemberStruct_Good_ {    int num;    char data[];} ArraryMemberStruct_Good;
        void demo6_good(){    // 打印結構體的大小    int size = sizeof(ArraryMemberStruct_Good);    printf("size = %d ", size);
           // 為結構體指針分配空間    ArraryMemberStruct_Good *ams = (ArraryMemberStruct_Good *)malloc(size + 1024);
           strcpy(ams->data, "hello");    printf("ams->data = %s ", ams->data);
           // 打印結構體指針、成員變量的地址    printf("ams = 0x%x ", ams);    printf("ams->num  = 0x%x ", &ams->num);    printf("ams->data = 0x%x ", ams->data);
           // 釋放空間    free(ams);}

        打印結果如下:

        與第一個例子中有下面幾個不同點:

        結構體的大小變成了 4;為結構體指針分配空間時,除了結構體本身的大小外,還申請了 data 需要的空間大小;不需要為 data 單獨分配空間了;釋放空間時,直接釋放結構體指針即可;

        是不是用起來簡單多了?!這就是柔性數組的好處。

        從語法上來說,柔性數組就是指結構體中最后一個元素個數未知的數組,也可以理解為長度為 0,那么就可以讓這個結構體稱為可變長的。

        前面說過,數組名就代表一個地址,是一個不變的地址常量。在結構體中,數組名僅僅是一個符號而已,只代表一個偏移量,不會占用具體的空間。

        另外,柔性數組可以是任意類型。這里示例大家多多體會,在很多通訊類的處理場景中,常常見到這種用法。

        7. 通過指針來獲取結構體中成員變量的偏移量

        這個標題讀起來似乎有點拗口,拆分一下:在一個結構體變量中,可以利用指針操作的技巧,獲取某個成員變量的地址、距離結構體變量的開始地址、之間的偏移量。

        在 Linux 內核代碼中你可以看到很多地方都利用了這個技巧,代碼如下:

        #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE*)0)->MEMBER))
        typedef struct _OffsetStruct_ {    int a;    int b;    int c;} OffsetStruct;
        void demo7(){    OffsetStruct os;    // 打印結構體變量、成員變量的地址    printf("&os = 0x%x ", &os);    printf("&os->a = 0x%x ", &os.a);    printf("&os->b = 0x%x ", &os.b);    printf("&os->c = 0x%x ", &os.c);    printf("===== ");    // 打印成員變量地址,與結構體變量開始地址,之間的偏移量    printf("offset: a = %d ", (char *)&os.a - (char *)&os);    printf("offset: b = %d ", (char *)&os.b - (char *)&os);    printf("offset: c = %d ", (char *)&os.c - (char *)&os);    printf("===== ");    // 通過指針的強制類型轉換來獲取偏移量    printf("offset: a = %d ", (size_t) &((OffsetStruct*)0)->a);    printf("offset: b = %d ", (size_t) &((OffsetStruct*)0)->b);    printf("offset: c = %d ", (size_t) &((OffsetStruct*)0)->c);    printf("===== ");    // 利用宏定義來得到成員變量的偏移量    printf("offset: a = %d ", offsetof(OffsetStruct, a));    printf("offset: b = %d ", offsetof(OffsetStruct, b));    printf("offset: c = %d ", offsetof(OffsetStruct, c));}

        先來看打印結果:

        前面 4 行的打印信息不需要解釋了,直接看下面這個內存模型即可理解。

        下面這個語句也不需要多解釋,就是把兩個地址的值進行相減,得到距離結構體變量開始地址的偏移量,注意:需要把地址強轉成 char* 型之后,才可以相減。

        printf("offset: a = %d ", (char *)&os.a - (char *)&os);

        下面這條語句需要好好理解:

        printf("offset: a = %d ", (size_t) &((OffsetStruct*)0)->a);

        數字 0 看成是一個地址,也就是一個指針。上篇文章解釋過,指針就代表內存中的一塊空間,至于你把這塊空間里的數據看作是什么,這個隨便你,你只要告訴編譯器,編譯器就按照你的意思去操作這些數據。

        現在我們把 0 這個地址里的數據看成是一個 OffsetStruct 結構體變量(通過強制轉換來告訴編譯器),這樣就得到了一個 OffsetStruct 結構體指針(下圖中綠色橫線),然后得到該指針變量中的成員變量 a(藍色橫線),再然后通過取地址符 & 得到 a 的地址(橙色橫線),最后把這個地址強轉成 size_t 類型(紅色橫線)。

        因為這個結構體指針變量是從 0 地址開始的,因此,成員變量 a 的地址就是 a 距離結構體變量開始地址的偏移量。

        上面的描述過程,如果感覺拗口,請結合下面這張圖再讀幾遍:

        上面這張圖如果能看懂的話,那么最后一種通過宏定義獲取偏移量的打印語句也就明白了,無非就是把代碼抽象成宏定義了,方便調用:

        #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE*)0)->MEMBER))
        printf("offset: a = %d ", offsetof(OffsetStruct, a));

        可能有小伙伴提出:獲取這個偏移量有什么用啊?那就請接著看下面的示例 8。

        8. 通過結構體中成員變量的指針,來獲取該結構體的指針

        標題同樣比較拗口,直接結合代碼來看:

        typedef struct _OffsetStruct_ {    int a;    int b;    int c;} OffsetStruct;

        假設有一個 OffsetStruct 結構體變量 os,我們只知道 os 中成員變量 c 的地址(指針),那么我們想得到變量 os 的地址(指針),應該怎么做?這就是標題所描述的目的。

        下面代碼中的宏定義 container_of 同樣是來自于 Linux 內核中的(大家平常沒事時多挖掘,可以發現很多好東西)。

        #define container_of(ptr, type, member) ({      const typeof( ((type *)0)->member ) *__mptr = (ptr);      (type *)( (char *)__mptr - offsetof(type,member) );})
        void demo8(){    // 下面 3 行僅僅是演示 typeof 關鍵字的用法    int n = 1;    typeof(n) m = 2;  // 定義相同類型的變量m    printf("n = %d, m = %d ", n, m);
           // 定義結構體變量,并初始化    OffsetStruct os = {1, 2, 3};        // 打印結構體變量的地址、成員變量的值(方便后面驗證)    printf("&os = 0x%x ", &os);    printf("os.a = %d, os.b = %d, os.c = %d ", os.a, os.b, os.c);
           printf("===== ");        // 假設只知道某個成員變量的地址    int *pc = &os.c;    OffsetStruct *p = NULL;        // 根據成員變量的地址,得到結構體變量的地址    p = container_of(pc, OffsetStruct, c);        // 打印指針的地址、成員變量的值    printf("p = 0x%x ", p);    printf("p->a = %d, p->b = %d, p->c = %d ", p->a, p->b, p->c);}

        先看打印結果:

        首先要清楚宏定義中參數的類型:

        ptr: 成員變量的指針;type: 結構體類型;member:成員變量的名稱;

        這里的重點就是理解宏定義 container_of,結合下面這張圖,把宏定義拆開來進行描述:

        宏定義中的第 1 條語句分析:

        綠色橫線:把數字 0 看成是一個指針,強轉成結構體 type 類型;藍色橫線:獲取該結構體指針中的成員變量 member;橙色橫線:利用 typeof 關鍵字,獲取該 member 的類型,然后定義這個類型的一個指針變量 __mptr;紅色橫線:把宏參數 ptr 賦值給 __mptr 變量;

        宏定義中的第 2 條語句分析:

        綠色橫線:利用 demo7 中的 offset 宏定義,得到成員變量 member 距離結構體變量開始地址的偏移量,而這個成員變量指針剛才已經知道了,就是 __mptr;藍色橫線:把 __mptr 這個地址,減去它自己距離結構體變量開始地址的偏移量,就得到了該結構體變量的開始地址;橙色橫線:最后把這個指針(此時是 char* 型),強轉成結構體 type 類型的指針;

        三、總結

        上面這 8 個關于指針的用法掌握之后,再去處理子字符、數組、鏈表等數據,基本上就是熟練度和工作量的問題了。希望大家都能用好指針這個神器,提高程序程序執行效率。

        面對代碼,永無bug;面對生活,春暖花開!祝您好運!

        原創不易,如果這篇文章有幫助,請轉發、分享給您的朋友,道哥在此表示感謝!

        【原創聲明】

        作者:道哥

        <上一頁  1  2  
        聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯系舉報。

        發表評論

        0條評論,0人參與

        請輸入評論內容...

        請輸入評論/評論長度6~500個字

        您提交的評論過于頻繁,請輸入驗證碼繼續

        暫無評論

        暫無評論

          人工智能 獵頭職位 更多
          掃碼關注公眾號
          OFweek人工智能網
          獲取更多精彩內容
          文章糾錯
          x
          *文字標題:
          *糾錯內容:
          聯系郵箱:
          *驗 證 碼:

          粵公網安備 44030502002758號

          主站蜘蛛池模板: 男人天堂一区| 徐闻县| 亚洲区中文字幕| 亚太无码| A片入口| 来凤县| 国产九九在线视频| 湖北省| 欧美老熟妇又粗又大| 中国老熟妇| 两个人看的www| 亚欧女AV| 高碑店市| 另类无码| 色道www| 亚洲成人性爱网| 一区二区淫网| ****亚洲成a人片第1集| 亚洲无码激情| 超碰人人干| 免费AV网站| 肥城市| 91成人视频在线观看| 久久国产影院| 亚洲小视频| 久草精品视频| 诸城市| 在线视频免费看3| 91乱子伦国产乱子伦!| 大熟女在线| 兖州市| 亚洲播播| 亚洲国产精品狼友在线观看| 中文字幕av一区| 欧美AA视频| 亚洲国产精品成人网站| 久久发布国产伦子伦精品| 武冈市| 伊人五月综合| 亚州精品熟女在线| 国产成人一区二区三区小说|