我們先來定義一下需求:
已知結構體型別定義如下:
struct node_t;
且結構體1byte對齊
#pragma pack(1)
求:結構體struct node_t中成員變數c的偏移。
注:這裡的偏移量指的是相對於結構體起始位置的偏移量。
有三種方法:
1.使用巨集offsetof()。
2.定義乙個結構體,【用結構體成員的位址】-【結構體起始位址】。
3.不去定義結構體,進行求解。
1.首先,我們看一下巨集offsetof的使用規則:
宣告:size_t offsetof( structname, membername );//第乙個引數是結構體名,第二個引數是結構體成員名字
定義:
#define offsetof(s,m) (size_t)&reinterpret_castvolatile
char&>((((s *)0)->m))
另外:static_cast和reinterpret_cast(強制型別轉換符)的區別主要在於 多重繼承, static_cast計算了父子類 指標轉換的 偏移量,並將之轉換到正確的位址(c裡面有m_a,m_b,轉換為b*指標後指到m_b處),而reinterpret_cast卻不會做這一層轉換。
我們先來定義乙個結構體變數node:
struct node_t node;
接著來計算成員變數c的偏移量:
(unsigned long)(&(node.c)) - (unsigned long)(&node)
&(node.c)為結構體成員變數c的位址,並強制轉化為unsigned long;
&node為結構體的起始位址,也強制轉化為unsigned long;
最後我們將上述兩值相減,得到成員變數c的偏移量;
2.我們先來定義第乙個結構體變數node:
struct node_t node;
接著計算成員變數c的偏移量:
轉化為無符號長整形進行計算(因為首先位址佔4個位元組,且第一為不為符號位,若兩個位址相加,無符號整形表示結果,顯然無法滿足。所以一般轉化為長整形,這樣會更安全一點。)
(unsigned
long)(&node.c)-(unsigned
long)(&node)
3.我們對第二種方法進行改進一下,不定義乙個結構體出來。
在**新的解決方法之前,我們先來**乙個有關偏移的小問題:
小問題這是一道簡單的幾何問題,假設在座標軸上由a點移動到b點,如何計算b相對於a的偏移?這個問題對於我們來說是非常的簡單,可能大部分人都會脫口而出並得到答案為b-a。
那麼這個答案是否完全準確呢?比較嚴謹的你覺得顯然不是,原因在於,當a為座標原點即a=0的時候,上述答案b-a就直接簡化為b了。
這個小小的簡單的問題,對於我們來說有什麼啟示呢?
我們結合方法2的思路和上述的小問題,是不是很快就得到了下面的關聯:
(unsigned long)(&(node.c)) - (unsigned long)(&node)
和b - a
我們小問題的思路是當a為座標原點的時候,b-a就簡化為b了,那麼對應到我們的方法2,當node的記憶體位址為0即(&node==0)的時候,上面的**可簡化為:
(unsigned long)(&(node.c))
由於node記憶體位址==0了,所以
node.c //結構體node中成員變數c
我們就可以使用另外一種方式來表達了,如下:
((struct node_t *)0)->c
述**應該比較好理解,由於我們知道結構體的記憶體位址編號為0,所以我們就可以直接通過記憶體位址的方式來訪問該結構體的成員變數,相應的**的含義就是 獲取記憶體位址編號為0的結構體struct node_t的成員變數c。
此時,我們的偏移求法就消除了struct node_t node這個自定義變數,直接一行**解決,:
(unsigned long)(&(((struct node_t *)0)->c))
上述的**相對於方法2是不是更簡潔了一些。
這裡我們將上面的**功能定義為乙個巨集,該巨集的作用是用來計算某結構體內成員變數的偏移(後面的示例會使用該巨集):
#define offset_of(type, member) (unsigned long)(&(((type *)0)->member))
使用上面的巨集,就可以直接得到成員變數c在結構體struct node_t中的偏移為:
offset_of(struct node_t, c)
和示例1一樣,我們先定義需求如下:
已知結構體型別定義如下:
struct node_t;
#pragma pack(1)
int *p_c,該指標指向struct node_t x的成員變數c
結構體1byte對齊求:
結構體x的成員變數b的值?
拿到這個問題的時候,我們先做一下簡單的分析,題目的意思是根據乙個指向某結構體成員變數的指標,如何求該結構體的另外乙個成員變數的值。
那麼可能的幾種解法有:
方法1 由於我們知道結構體是1byte對齊的,所以這道題最簡單的解法是:
(int )((unsigned long)p_c - sizeof(int))
上述**很簡單,成員變數c的位址減去sizeof(int)從而得到成員變數b的位址,然後再強制轉換為int *,最後再取值最終得到成員變數b的值;
方法2 方法1的**雖然簡單,但擴充套件性不夠好。我們希望通過p_c直接得到指向該結構體的指標p_node,然後通過p_node訪問該結構體的任意成員變數了。
【成員變數c的位址p_c】減去【c在結構體中的偏移】
由示例1,我們得到結構體struct node_t中成員變數c的偏移為:
(struct node_t )((unsigned long)p_c - (unsigned long)(&((struct node_t )0)->c))
我們也可以直接使用示例1中定義的offset_of巨集,則上面的**變為:
(struct node_t *)((unsigned long)p_c - offset_of(struct node_t, c))
最後我們就可以使用下面的**來獲取成員變數a,b的值:
p_node->a
p_node->b
我們同樣將上述**的功能定義為如下巨集:
我們使用上面的巨集來修改之前的**如下:
struct_entry(p_c, struct node_t, c)
p_c為指向結構體struct node_t成員變數c的指標;
struct node_t結構體型別;
#define struct_entry(ptr, type, member) (
type *)((unsigned long)(ptr)-offset_of(type, member)) //該巨集的功能是通過結構體任意成員變數的指標來獲得指向該結構體的指標。
c為p_c指向的成員變數;
注:int a = 10;
int * p_a = &a;
設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
從上述三種情況,相信你應該能體會到我所要表達的意思了。(注:後續某博文將從編譯器的角度對該問題進行詳細的闡述)
C C 結構體成員偏移量獲取
分析 節選自muduo.以下 通過offsetof獲取sin family在sockaddr in6中的字段偏移量.static assert offsetof sockaddr in6,sin6 family 0,sin6 family offset 0 需要注意 offsetof並非c c 標準...
計算結構體偏移量
如果能夠讓 unsigned long type 的值為0,即 type 0的時候,那麼offset的值就是簡單的 offset unsigned long type.c 如果說 type 0,那麼type.c就可以等價於 type t 0 c。但是這個語句是不能單獨存在的,因為對null指標訪問成...
結構體成員的偏移量與offsetof函式
1.一旦結構體定義下來,則結構體中的成員記憶體布局就定下了。typedef struct test testmem void test 2.offsetof函式 巨集函式offset,用於求結構體中的乙個成員,在結構體中的偏移量 在stddef.h 標頭檔案中,該巨集的完整說明如下 ifdef cp...