成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于C語言結構體偏移的一點思考

開發 后端 開發工具
這篇博文可以幫你更好的理解這個問題c語言中兩種宏定義的區別,關于這個思考有哪些方面的意義,細心的你可能發現本文所屬的類別為linux內核源碼分析,而并非 GNU C語言編程,可能有些同學會有些許好奇。不過不用著急,如果對本篇博文意義感興趣的同學,可繼續關注后續的博文,會有進一步的闡述。

前言

相信大家在c語言程序開發的過程一定都使用過結構體,那么不知你對結構體中成員變量偏移這塊是如何理解的?本文將和大家一起分享下,本人最近關于c語言中結構體偏移的一些思考和總結。

另外這篇博文還可以幫你更好的理解這個問題c語言中兩種宏定義的區別,關于這個思考有哪些方面的意義,細心的你可能發現本文所屬的類別為linux內核源碼分析,而并非 GNU C語言編程,可能有些同學會有些許好奇不過不用著急,如果對本篇博文意義感興趣的同學,可繼續關注后續的博文,會有進一步的闡述。

示例1

我們先來定義一下需求:

已知結構體類型定義如下:

  1. struct node_t{  
  2.     char a;  
  3.     int b;  
  4.     int c;  
  5. }; 

且結構體1Byte對齊

  1. #pragma pack(1) 

求:

結構體struct node_t中成員變量c的偏移。

:這里的偏移量指的是相對于結構體起始位置的偏移量。

看到這個問題的時候,我相信不同的人腦中浮現的解決方法可能會有所差異,下面我們分析以下幾種可能解法:

方法1

如果你對c語言的庫函數比較熟悉的話,那么你第一個想到的肯定是offsetof函數(其實只是個宏而已,先姑且這樣叫著吧),我們man 3 offsetof查看函數原型如下:

  1. #include <stddef.h> 
  2.       size_t offsetof(type, member); 

有了上述的庫函數,我們用一行代碼就可以搞定:

  1. offsetof(struct node_t, c); 

當然這并非本文探討的重點,請繼續閱讀。

方法2

當我們對c語言的庫函數不熟悉的時候,此時也不要著急,我們依然可以使用我們自己的方法來解決問題。

最直接的思路是:結構體成員變量c的地址 - 結構體起始地址

我們先來定義一個結構體變量node:

  1. struct node_t node; 

接著來計算成員變量c的偏移量:

  1. (unsigned long)(&(node.c)) - (unsigned long)(&node) 

&(node.c)為結構體成員變量c的地址,并強制轉化為unsigned long;

&node為結構體的起始地址,也強制轉化為unsigned long;

最后我們將上述兩值相減,得到成員變量c的偏移量;

方法3

按照方法2的思路我們在不借助庫函數的情況下,依然可以得到成員變量c的偏移量。但作為程序員,我們應該善于思考,是不是可以針對上面的代碼做一些改進,使我們的代碼變得更簡潔一些?在做具體的改進之前,我們應該分析方法2存在哪些方面的問題。

相信不用我多說,細心的你一定已經察覺到,方法2中最主要的一個問題是我們自定義了一個結構體變量node,雖然題目中并未限制我們可以自定義變量,但當我們遇到比較嚴且題目中不允許自定義變量的時候,此時我們就要思考新的解決方法。

在探討新的解決方法之前,我們先來探討一個有關偏移的小問題:

小問題

這是一道簡單的幾何問題,假設在座標軸上由A點移動到B點,如何計算B相對于A的偏移?這個問題對于我們來說是非常的簡單,可能大部分人都會脫口而出并得到答案為B-A。

那么這個答案是否完全準確呢?比較嚴謹的你覺得顯然不是,原因在于,當A為坐標原點即A=0的時候,上述答案B-A就直接簡化為B了。

這個小小的簡單的問題,對于我們來說有什么啟示呢?

我們結合方法2的思路和上述的小問題,是不是很快就得到了下面的關聯:

  1. (unsigned long)(&(node.c)) - (unsigned long)(&node) 

  1. B - A 

我們小問題的思路是當A為坐標原點的時候,B-A就簡化為B了,那么對應到我們的方法2,當node的內存地址為0即(&node==0)的時候,上面的代碼可簡化為:

  1. (unsigned long)(&(node.c))  

由于node內存地址==0了,所以

  1. node.c      //結構體node中成員變量c 

我們就可以使用另外一種方式來表達了,如下:

  1. ((struct node_t *)0)->c 

上述代碼應該比較好理解,由于我們知道結構體的內存地址編號為0,所以我們就可以直接通過內存地址的方式來訪問該結構體的成員變量,相應的代碼的含義就是 獲取內存地址編號為0的結構體struct node_t的成員變量c。

此時,我們的偏移求法就消除了struct node_t node這個自定義變量,直接一行代碼解決,:

  1. (unsigned long)(&(((struct node_t *)0)->c)) 

上述的代碼相對于方法2是不是更簡潔了一些。

這里我們將上面的代碼功能定義為一個宏,該宏的作用是用來計算某結構體內成員變量的偏移(后面的示例會使用該宏):

  1. #define OFFSET_OF(type, member) (unsigned long)(&(((type *)0)->member)) 

使用上面的宏,就可以直接得到成員變量c在結構體struct node_t中的偏移為:

  1. OFFSET_OF(struct node_t, c) 

示例2

和示例1一樣,我們先定義需求如下:

已知結構體類型定義如下:

  1. struct node_t{  
  2.     char a;  
  3.     int b;  
  4.     int c;  
  5. }; 

int *p_c,該指針指向struct node_t x的成員變量c

結構體1Byte對齊

  1. #pragma pack(1) 

求:

結構體x的成員變量b的值?

拿到這個問題的時候,我們先做一下簡單的分析,題目的意思是根據一個指向某結構體成員變量的指針,如何求該結構體的另外一個成員變量的值。

那么可能的幾種解法有:

方法1

由于我們知道結構體是1Byte對齊的,所以這道題最簡單的解法是:

  1. *(int *)((unsigned long)p_c - sizeof(int)) 

上述代碼很簡單,成員變量c的地址減去sizeof(int)從而得到成員變量b的地址,然后再強制轉換為int *,最后再取值最終得到成員變量b的值;

方法2

方法1的代碼雖然簡單,但擴展性不夠好。我們希望通過p_c直接得到指向該結構體的指針p_node,然后通過p_node訪問該結構體的任意成員變量了。

由此我們得到計算結構體起始地址p_node的思路為:

(成員變量c的地址p_c)減去(c在結構體中的偏移)

由示例1,我們得到結構體struct node_t中成員變量c的偏移為:

  1. (unsigned long)&(((struct node_t *)0)->c) 

所以我們得到結構體的起始地址指針p_node為:

  1. (struct node_t *)((unsigned long)p_c - (unsigned long)(&((struct node_t *)0)->c)) 

我們也可以直接使用示例1中定義的OFFSET_OF宏,則上面的代碼變為:

  1. (struct node_t *)((unsigned long)p_c - OFFSET_OF(struct node_t, c)) 

最后我們就可以使用下面的代碼來獲取成員變量a,b的值:

  1. p_node->a  
  2. p_node->b 

我們同樣將上述代碼的功能定義為如下宏:

  1. #define STRUCT_ENTRY(ptr, type, member) (type *)((unsigned long)(ptr)-OFFSET_OF(type, member)) 

該宏的功能是通過結構體任意成員變量的指針來獲得指向該結構體的指針。

我們使用上面的宏來修改之前的代碼如下:

  1. STRUCT_ENTRY(p_c, struct node_t, c) 
  • p_c為指向結構體struct node_t成員變量c的指針;
  • struct node_t結構體類型;
  • c為p_c指向的成員變量;

注:

上述關于地址運算的一些說明:

  1. int a = 10;  
  2. int * p_a = &a; 

 

結論

本文通過幾個示例描述了c語言結構體有關偏移的一些有意思的事情,希望能夠對你有所幫助。為什么會有上述思考,相信有些同學已經看出一些端倪,這也正是后續博文將要描述的主題。

 

設p_a == 0x95734104;

以下為編譯器計算的相關結果: 

p_a + 10 == p_a + sizeof(int)*10 =0x95734104 + 4*10 = 0x95734144

(unsigned long)p_a + 10 == 0x95734104+10 = 0x95734114

(char *)p_a + 10 == 0x95734104 + sizeof(char)*10 = 0x95734114

從上述三種情況,相信你應該能體會到我所要表達的意思了。

原文鏈接:http://my.oschina.net/gschen/blog/140463

責任編輯:林師授 來源: OSCHINA
相關推薦

2011-12-23 09:16:19

2011-07-18 16:33:20

sqlite

2022-04-06 07:14:29

區塊鏈網絡生態系統

2011-07-04 09:33:04

惠普轉型李艾科

2012-03-27 08:49:19

Json

2024-04-28 14:54:09

機器人代碼

2021-11-28 22:57:41

C語言STM32寄存器

2009-01-20 14:04:58

數據挖掘信息整合應用

2014-06-04 10:48:38

Swift蘋果iOS

2012-07-12 10:49:53

項目管理

2009-08-14 11:05:28

C#語言的結構體

2014-09-17 10:30:25

代碼

2023-11-08 10:14:02

模型學習

2014-02-10 15:05:37

C語言封裝

2021-05-17 11:47:41

多租戶系統私有化

2022-08-19 14:38:52

C語言結構體struct

2010-12-30 09:22:58

C語言 數組

2009-08-03 09:17:30

ASP.NET Ses

2011-07-14 16:17:47

AmoebaMySQL

2015-03-26 09:23:17

天璣科技融合架構
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩色视频| 久久大| 中文字幕1区2区3区 日韩在线视频免费观看 | 亚洲精品久久久一区二区三区 | 欧美久久一区二区三区 | 精品欧美一区免费观看α√ | 欧美视频三级 | 国产精品中文字幕在线观看 | 玖玖免费| 欧美日韩不卡合集视频 | 亚洲成人高清 | 亚洲在线久久 | 欧美日韩在线视频一区二区 | 亚洲一区日韩 | 欧美视频在线观看 | 羞羞视频在线观看 | 天天操 天天操 | 国产农村一级片 | 精品视频在线免费观看 | 91av大全 | 一级做a爰片久久毛片免费看 | 成人黄页在线观看 | 欧美精品在线一区二区三区 | 在线观看免费av网 | 久久久九九九九 | 国产欧美日韩一区 | 在线亚洲一区 | 狠狠爱视频 | 在线播放日韩 | 成人在线a| 久久亚洲一区二区三区四区 | 久久精品国产一区二区电影 | 精品动漫一区 | 久久免费精品 | 久久精品av麻豆的观看方式 | 天天久久 | 亚洲精品一二三区 | 国产高清精品一区二区三区 | 操亚洲 | 亚洲女人天堂成人av在线 | 91久久夜色 |