自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C指針的這些使用技巧,掌握后立刻提升一個Level

開發(fā) 前端
今天主要是介紹指針在應(yīng)用程序的編程中,經(jīng)常使用的技巧。

 

一、前言

半個月前寫的那篇關(guān)于指針最底層原理的文章,得到了很多朋友的認(rèn)可(鏈接: C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹),特別是對剛學(xué)習(xí)C語言的小伙伴來說,很容易就從根本上理解指針到底是什么、怎么用,這也讓我堅信一句話;用心寫出的文章,一定會被讀者感受到!在寫這篇文章的時候,我列了一個提綱,寫到后面的時候,發(fā)現(xiàn)已經(jīng)超過一萬字了,但是提綱上還有最后一個主題沒有寫。如果繼續(xù)寫下去,文章體積就太大了,于是就留下了一個尾巴。

今天,我就把這個尾巴給補(bǔ)上去:主要是介紹指針在應(yīng)用程序的編程中,經(jīng)常使用的技巧。如果之前的那篇文章勉強(qiáng)算是“道”層面的話,那這篇文章就屬于“術(shù)”的層面。主要通過 8 個示例程序來展示在 C 語言應(yīng)用程序中,關(guān)于指針使用的常見套路,希望能給你帶來收獲。

記得我在校園里學(xué)習(xí)C語言的時候,南師大的黃鳳良老師花了大半節(jié)課的時間給我們解釋指針,現(xiàn)在最清楚地記得老師說過的一句話就是:指針就是地址,地址就是指針!

二、八個示例

1. 開胃菜:修改主調(diào)函數(shù)中的數(shù)據(jù)

  1. // 交換 2 個 int 型數(shù)據(jù) 
  2. void demo1_swap_data(int *a, int *b) 
  3.     int tmp = *a; 
  4.     *a = *b; 
  5.     *b = tmp; 
  6.  
  7. void demo1() 
  8.     int i = 1; 
  9.     int j = 2; 
  10.     printf("before: i = %d, j = %d \n", i, j); 
  11.     demo1_swap_data(&i, &j); 
  12.     printf("after:  i = %d, j = %d \n", i, j); 

這個代碼不用解釋了,大家一看就明白。如果再過多解釋的話,好像在侮辱智商。

2. 在被調(diào)用函數(shù)中,分配系統(tǒng)資源

代碼的目的是:在被調(diào)用函數(shù)中,從堆區(qū)分配 size 個字節(jié)的空間,返回給主調(diào)函數(shù)中的 pData 指針。

  1. void demo2_malloc_heap_error(char *buf, int size
  2.     buf = (char *)malloc(size); 
  3.     printf("buf = 0x%x \n", buf); 
  4.  
  5. void demo2_malloc_heap_ok(char **buf, int size
  6.     *buf = (char *)malloc(size); 
  7.     printf("*buf = 0x%x \n", *buf); 
  8.  
  9. void demo2() 
  10.     int size = 1024; 
  11.     char *pData = NULL
  12.  
  13.     // 錯誤用法 
  14.     demo2_malloc_heap_error(pData, size); 
  15.     printf("&pData = 0x%x, pData = 0x%x \n", &pData, pData); 
  16.  
  17.     // 正確用法 
  18.     demo2_malloc_heap_ok(&pData, size); 
  19.     printf("&pData = 0x%x, pData = 0x%x \n", &pData, pData); 
  20.     free(pData); 

2.1 錯誤用法

剛進(jìn)入被調(diào)用函數(shù) demo2_malloc_heap_error 的時候,形參 buff 是一個 char* 型指針,它的值等于 pData 變量的值,也就是說 buff 與 pData 的值相同(都為 NULL),內(nèi)存模型如圖:


在被調(diào)用函數(shù)中執(zhí)行 malloc 語句之后,從堆區(qū)申請得到的地址空間賦值給 buf,就是說它就指向了這個新的地址空間,而 pData 里仍然是NULL,內(nèi)存模型如下:


從圖中可以看到,pData 的內(nèi)存中一直是 NULL,沒有指向任何堆空間。另外,由于形參 buf 是放在函數(shù)的棧區(qū)的,從被調(diào)函數(shù)中返回的時候,堆區(qū)這塊申請的空間就被泄漏了。

2.2 正確用法

剛進(jìn)入被調(diào)用函數(shù) demo2_malloc_heap_error 的時候,形參 buf 是一個 char* 型的二級指針,就是說 buf 里的值是另一個指針變量的地址,在這個示例中 buf 里的值就是 pData 這個指針變量的地址,內(nèi)存模型如下:


在被調(diào)用函數(shù)中執(zhí)行 malloc 語句之后,從堆區(qū)申請得到的地址空間賦值給 *buf,因為 buf = &pData,所以 *buf 就相當(dāng)于是 pData,那么從堆區(qū)申請得到的地址空間就賦值 pData 變量,內(nèi)存模型如下:


從被調(diào)函數(shù)中返回之后,pData 就正確的得到了一塊堆空間,別忘了使用之后要主動釋放。

3. 傳遞函數(shù)指針

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

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

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

  • 降序排列: demo3_algorithm_decend;
  • 升序排列: demo3_algorithm_ascend;
  1. typedef int BOOL; 
  2. #define FALSE 0 
  3. #define TRUE  1 
  4.  
  5. BOOL demo3_algorithm_decend(int a, int b) 
  6.     return a > b; 
  7.  
  8. BOOL demo3_algorithm_ascend(int a, int b) 
  9.     return a < b; 
  10.  
  11. typedef BOOL (*Func)(intint); 
  12. void demo3_handle_data(int *data, int size, Func pf) 
  13.     for (int i = 0; i < size - 1; ++i) 
  14.     { 
  15.         for (int j = 0; j < size - 1 - i; ++j) 
  16.         { 
  17.             // 調(diào)用傳入的排序函數(shù) 
  18.             if (pf(data[j], data[j+1])) 
  19.             { 
  20.                 int tmp = data[j]; 
  21.                 data[j] = data[j + 1]; 
  22.                 data[j + 1] = tmp; 
  23.             } 
  24.         } 
  25.     } 
  26.  
  27. void demo3() 
  28.     int a[5] = {5, 1, 9, 2, 6}; 
  29.     int size = sizeof(a)/sizeof(int); 
  30.     // 調(diào)用排序函數(shù),需要傳遞排序算法函數(shù) 
  31.     //demo3_handle_data(a, size, demo3_algorithm_decend); // 降序排列 
  32.     demo3_handle_data(a, size, demo3_algorithm_ascend);   // 升序排列 
  33.     for (int i = 0; i < size; ++i) 
  34.         printf("%d ", a[i]); 
  35.     printf("\n"); 

這個就不用畫圖了,函數(shù)指針 pf 就指向了傳入的那個函數(shù)地址,在排序的時候
直接調(diào)用就可以了。

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

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

當(dāng)處理一條控制指令時,先用一個通用命令(指令頭)的指針來接收指令,然后根據(jù)命令類型枚舉變量來區(qū)分,把控制指令強(qiáng)制轉(zhuǎn)換成具體的那個設(shè)備的數(shù)據(jù)結(jié)構(gòu),這樣就可以獲取到控制指令中特定的控制數(shù)據(jù)了。

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

  1. // 指令類型枚舉 
  2. typedef enum _CMD_TYPE_ { 
  3.     CMD_TYPE_CONTROL_SWITCH = 1, 
  4.     CMD_TYPE_CONTROL_LAMP, 
  5. } CMD_TYPE; 
  6.  
  7. // 通用的指令數(shù)據(jù)結(jié)構(gòu)(指令頭) 
  8. typedef struct _CmdBase_ { 
  9.     CMD_TYPE cmdType; // 指令類型 
  10.     int deviceId;     // 設(shè)備 Id 
  11. } CmdBase; 
  12.  
  13. typedef struct _CmdControlSwitch_ { 
  14.     // 前 2 個參數(shù)是指令頭 
  15.     CMD_TYPE cmdType;    
  16.     int deviceId; 
  17.      
  18.     // 下面都有這個指令私有的數(shù)據(jù) 
  19.     int slot;  // 排插上的哪個插口 
  20.     int state; // 0:斷開, 1:接通 
  21. } CmdControlSwitch; 
  22.  
  23. typedef struct _CmdControlLamp_ { 
  24.     // 前 2 個參數(shù)是指令頭 
  25.     CMD_TYPE cmdType; 
  26.     int deviceId; 
  27.      
  28.     // 下面都有這個指令私有的數(shù)據(jù) 
  29.     int color;      // 顏色 
  30.     int brightness; // 亮度 
  31. } CmdControlLamp; 
  32.  
  33. // 參數(shù)是指令頭指針 
  34. void demo4_control_device(CmdBase *pcmd) 
  35.     // 根據(jù)指令頭中的命令類型,把指令強(qiáng)制轉(zhuǎn)換成具體設(shè)備的指令 
  36.     if (CMD_TYPE_CONTROL_SWITCH == pcmd->cmdType) 
  37.     { 
  38.         // 類型強(qiáng)制轉(zhuǎn)換 
  39.         CmdControlSwitch *cmd = pcmd; 
  40.         printf("control switch. slot = %d, state = %d \n", cmd->slot, cmd->state); 
  41.     } 
  42.     else if (CMD_TYPE_CONTROL_LAMP == pcmd->cmdType) 
  43.     { 
  44.         // 類型強(qiáng)制轉(zhuǎn)換 
  45.         CmdControlLamp * cmd = pcmd; 
  46.         printf("control lamp.   color = 0x%x, brightness = %d \n", cmd->color, cmd->brightness); 
  47.     } 
  48.  
  49. void demo4() 
  50.     // 指令1:控制一個開關(guān) 
  51.     CmdControlSwitch cmd1 = {CMD_TYPE_CONTROL_SWITCH, 1, 3, 0}; 
  52.     demo4_control_device(&cmd1); 
  53.  
  54.     // 指令2:控制一個燈泡 
  55.     CmdControlLamp cmd2 = {CMD_TYPE_CONTROL_LAMP, 2, 0x112233, 90}; 
  56.     demo4_control_device(&cmd2); 

5. 函數(shù)指針數(shù)組

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

  1. int add(int a, int b) { return a + b; } 
  2. int sub(int a, int b) { return a - b; } 
  3. int mul(int a, int b) { return a * b; } 
  4. int divide(int a, int b) { return a / b; } 
  5.  
  6. void demo5() 
  7.     int a = 4, b = 2; 
  8.     int (*p[4])(intint); 
  9.     p[0] = add
  10.     p[1] = sub; 
  11.     p[2] = mul; 
  12.     p[3] = divide; 
  13.     printf("%d + %d = %d \n", a, b, p[0](a, b)); 
  14.     printf("%d - %d = %d \n", a, b, p[1](a, b)); 
  15.     printf("%d * %d = %d \n", a, b, p[2](a, b)); 
  16.     printf("%d / %d = %d \n", a, b, p[3](a, b)); 

6. 在結(jié)構(gòu)體中使用柔性數(shù)組

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

  1. // 一個結(jié)構(gòu)體,成員變量 data 是指針 
  2. typedef struct _ArraryMemberStruct_NotGood_ { 
  3.     int num; 
  4.     char *data; 
  5. } ArraryMemberStruct_NotGood; 
  6.  
  7. void demo6_not_good() 
  8.     // 打印結(jié)構(gòu)體的內(nèi)存大小 
  9.     int size = sizeof(ArraryMemberStruct_NotGood); 
  10.     printf("size = %d \n"size); 
  11.  
  12.     // 分配一個結(jié)構(gòu)體指針 
  13.     ArraryMemberStruct_NotGood *ams = (ArraryMemberStruct_NotGood *)malloc(size); 
  14.     ams->num = 1; 
  15.  
  16.     // 為結(jié)構(gòu)體中的 data 指針分配空間 
  17.     ams->data = (char *)malloc(1024); 
  18.     strcpy(ams->data, "hello"); 
  19.     printf("ams->data = %s \n", ams->data); 
  20.  
  21.     // 打印結(jié)構(gòu)體指針、成員變量的地址 
  22.     printf("ams = 0x%x \n", ams); 
  23.     printf("ams->num  = 0x%x \n", &ams->num); 
  24.     printf("ams->data = 0x%x \n", ams->data); 
  25.  
  26.     // 釋放空間 
  27.     free(ams->data); 
  28.     free(ams); 

在我的電腦上,打印結(jié)果如下:


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

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

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

  1. // 一個結(jié)構(gòu)體,成員變量是未指明大小的數(shù)組 
  2. typedef struct _ArraryMemberStruct_Good_ { 
  3.     int num; 
  4.     char data[]; 
  5. } ArraryMemberStruct_Good; 
  6.  
  7. void demo6_good() 
  8.     // 打印結(jié)構(gòu)體的大小 
  9.     int size = sizeof(ArraryMemberStruct_Good); 
  10.     printf("size = %d \n"size); 
  11.  
  12.     // 為結(jié)構(gòu)體指針分配空間 
  13.     ArraryMemberStruct_Good *ams = (ArraryMemberStruct_Good *)malloc(size + 1024); 
  14.  
  15.     strcpy(ams->data, "hello"); 
  16.     printf("ams->data = %s \n", ams->data); 
  17.  
  18.     // 打印結(jié)構(gòu)體指針、成員變量的地址 
  19.     printf("ams = 0x%x \n", ams); 
  20.     printf("ams->num  = 0x%x \n", &ams->num); 
  21.     printf("ams->data = 0x%x \n", ams->data); 
  22.  
  23.     // 釋放空間 
  24.     free(ams); 

打印結(jié)果如下:


與第一個例子中有下面幾個不同點(diǎn):

  1. 結(jié)構(gòu)體的大小變成了 4;
  2. 為結(jié)構(gòu)體指針分配空間時,除了結(jié)構(gòu)體本身的大小外,還申請了 data 需要的空間大小;
  3. 不需要為 data 單獨(dú)分配空間了;
  4. 釋放空間時,直接釋放結(jié)構(gòu)體指針即可;

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

  • 從語法上來說,柔性數(shù)組就是指結(jié)構(gòu)體中最后一個元素個數(shù)未知的數(shù)組,也可以理解為長度為 0,那么就可以讓這個結(jié)構(gòu)體稱為可變長的。
  • 前面說過,數(shù)組名就代表一個地址,是一個不變的地址常量。在結(jié)構(gòu)體中,數(shù)組名僅僅是一個符號而已,只代表一個偏移量,不會占用具體的空間。

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

7. 通過指針來獲取結(jié)構(gòu)體中成員變量的偏移量

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

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

  1. #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE*)0)->MEMBER)) 
  2.  
  3. typedef struct _OffsetStruct_ { 
  4.     int a; 
  5.     int b; 
  6.     int c; 
  7. } OffsetStruct; 
  8.  
  9. void demo7() 
  10.     OffsetStruct os; 
  11.     // 打印結(jié)構(gòu)體變量、成員變量的地址 
  12.     printf("&os = 0x%x \n", &os); 
  13.     printf("&os->a = 0x%x \n", &os.a); 
  14.     printf("&os->b = 0x%x \n", &os.b); 
  15.     printf("&os->c = 0x%x \n", &os.c); 
  16.     printf("===== \n"); 
  17.     // 打印成員變量地址,與結(jié)構(gòu)體變量開始地址,之間的偏移量 
  18.     printf("offset: a = %d \n", (char *)&os.a - (char *)&os); 
  19.     printf("offset: b = %d \n", (char *)&os.b - (char *)&os); 
  20.     printf("offset: c = %d \n", (char *)&os.c - (char *)&os); 
  21.     printf("===== \n"); 
  22.     // 通過指針的強(qiáng)制類型轉(zhuǎn)換來獲取偏移量 
  23.     printf("offset: a = %d \n", (size_t) &((OffsetStruct*)0)->a); 
  24.     printf("offset: b = %d \n", (size_t) &((OffsetStruct*)0)->b); 
  25.     printf("offset: c = %d \n", (size_t) &((OffsetStruct*)0)->c); 
  26.     printf("===== \n"); 
  27.     // 利用宏定義來得到成員變量的偏移量 
  28.     printf("offset: a = %d \n", offsetof(OffsetStruct, a)); 
  29.     printf("offset: b = %d \n", offsetof(OffsetStruct, b)); 
  30.     printf("offset: c = %d \n", offsetof(OffsetStruct, c)); 

先來看打印結(jié)果:

 

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


下面這個語句也不需要多解釋,就是把兩個地址的值進(jìn)行相減,得到距離結(jié)構(gòu)體變量開始地址的偏移量,注意:需要把地址強(qiáng)轉(zhuǎn)成 char* 型之后,才可以相減。

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

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

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

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

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

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

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


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

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

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

8. 通過結(jié)構(gòu)體中成員變量的指針,來獲取該結(jié)構(gòu)體的指針

標(biāo)題同樣比較拗口,直接結(jié)合代碼來看:

  1. typedef struct _OffsetStruct_ { 
  2.     int a; 
  3.     int b; 
  4.     int c; 
  5. } OffsetStruct; 

假設(shè)有一個 OffsetStruct 結(jié)構(gòu)體變量 os,我們只知道 os 中成員變量 c 的地址(指針),那么我們想得到變量 os 的地址(指針),應(yīng)該怎么做?這就是標(biāo)題所描述的目的。

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

  1. #define container_of(ptr, type, member) ({ \ 
  2.      const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 
  3.      (type *)( (char *)__mptr - offsetof(type,member) );}) 
  4.  
  5. void demo8() 
  6.     // 下面 3 行僅僅是演示 typeof 關(guān)鍵字的用法 
  7.     int n = 1; 
  8.     typeof(n) m = 2;  // 定義相同類型的變量m 
  9.     printf("n = %d, m = %d \n", n, m);  
  10.  
  11.     // 定義結(jié)構(gòu)體變量,并初始化 
  12.     OffsetStruct os = {1, 2, 3}; 
  13.      
  14.     // 打印結(jié)構(gòu)體變量的地址、成員變量的值(方便后面驗證) 
  15.     printf("&os = 0x%x \n", &os); 
  16.     printf("os.a = %d, os.b = %d, os.c = %d \n", os.a, os.b, os.c); 
  17.  
  18.     printf("===== \n"); 
  19.      
  20.     // 假設(shè)只知道某個成員變量的地址 
  21.     int *pc = &os.c; 
  22.     OffsetStruct *p = NULL
  23.      
  24.     // 根據(jù)成員變量的地址,得到結(jié)構(gòu)體變量的地址 
  25.     p = container_of(pc, OffsetStruct, c); 
  26.      
  27.     // 打印指針的地址、成員變量的值 
  28.     printf("p = 0x%x \n", p); 
  29.     printf("p->a = %d, p->b = %d, p->c = %d \n", p->a, p->b, p->c); 

先看打印結(jié)果:


首先要清楚宏定義中參數(shù)的類型:

  1. ptr: 成員變量的指針;
  2. type: 結(jié)構(gòu)體類型;
  3. member:成員變量的名稱;

這里的重點(diǎn)就是理解宏定義 container_of,結(jié)合下面這張圖,把宏定義拆開來進(jìn)行描述:


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

  1. 綠色橫線:把數(shù)字 0 看成是一個指針,強(qiáng)轉(zhuǎn)成結(jié)構(gòu)體 type 類型;
  2. 藍(lán)色橫線:獲取該結(jié)構(gòu)體指針中的成員變量 member;
  3. 橙色橫線:利用 typeof 關(guān)鍵字,獲取該 member 的類型,然后定義這個類型的一個指針變量 __mptr;
  4. 紅色橫線:把宏參數(shù) ptr 賦值給 __mptr 變量;

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

  1. 綠色橫線:利用 demo7 中的 offset 宏定義,得到成員變量 member 距離結(jié)構(gòu)體變量開始地址的偏移量,而這個成員變量指針剛才已經(jīng)知道了,就是 __mptr;
  2. 藍(lán)色橫線:把 __mptr 這個地址,減去它自己距離結(jié)構(gòu)體變量開始地址的偏移量,就得到了該結(jié)構(gòu)體變量的開始地址;
  3. 橙色橫線:最后把這個指針(此時是 char* 型),強(qiáng)轉(zhuǎn)成結(jié)構(gòu)體 type 類型的指針;

三、總結(jié)

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

 

責(zé)任編輯:姜華 來源: IOT物聯(lián)網(wǎng)小鎮(zhèn)
相關(guān)推薦

2021-09-09 17:05:36

C++智能指針語言

2024-08-13 08:22:04

緩存機(jī)制C#內(nèi)存緩存工具

2010-01-28 13:57:19

C++指針基礎(chǔ)

2025-01-10 08:38:16

2022-05-30 00:04:16

開源Github技巧

2021-06-16 17:46:55

函數(shù)指針結(jié)構(gòu)

2018-04-10 14:36:18

數(shù)據(jù)庫MySQL優(yōu)化技巧

2017-07-19 16:34:44

筆記本拆解技巧

2023-11-28 12:19:49

C++函數(shù)指針

2025-02-10 10:38:24

2024-08-06 12:35:42

C#代碼重構(gòu)

2017-03-07 14:26:19

Eclipse技巧效率

2016-12-13 17:02:49

androidjava移動應(yīng)用開發(fā)

2020-03-23 10:51:40

面試技巧技術(shù)

2021-01-12 11:44:48

java垃圾回收

2024-04-12 08:28:38

優(yōu)化查詢語句PostgreSQL索引

2021-05-11 12:30:21

PyTorch代碼Python

2024-09-12 17:39:27

2023-02-17 08:14:29

C語言C技巧內(nèi)存

2025-03-10 00:00:50

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號