詳解C說話的構造體中成員變量偏移成績。本站提示廣大學習愛好者:(詳解C說話的構造體中成員變量偏移成績)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C說話的構造體中成員變量偏移成績正文
c說話中關於構造體的地位偏移准繩簡略,但常常忘卻,做點筆記所以個記憶的好方法
准繩有三個:
a.構造體中的一切成員其首地址偏移量必需為器數據類型長度的整數被,個中第一個成員的首地址偏移量為0,
例如,若第二個成員類型為int,則其首地址偏移量必需為4的倍數,不然就要“首部填充”;以此類推
b.構造體所占的總字節數即sizeof()函數前往的值必需是最年夜成員的長度的整數倍,不然要停止“末尾填充”;
c.若構造體A將構造體B作為其成員,則構造體B存儲的首地址的偏移量必需為B中所含成員數據長度最年夜值的整數倍,
如若B中成員為int,double,char,則B的偏移量要為8的整數倍;不然停止“中央填充”。
信任年夜家在c說話法式開辟的進程必定都應用過構造體,那末不知你對構造體中成員變量偏移這塊是若何懂得的?本文將和年夜家一路分享下,自己比來關於c說話中構造體偏移的一些思慮和總結。
示例1
我們先來界說一下需求:
已知構造體類型界說以下:
struct node_t{ char a; int b; int c; };
且構造體1Byte對齊
#pragma pack(1)
求:
構造體struct node_t中成員變量c的偏移。
注:這裡的偏移量指的是絕對於構造體肇端地位的偏移量。
看到這個成績的時刻,我信任分歧的人腦中顯現的處理辦法能夠會有所差別,上面我們剖析以下幾種能夠的解法:
辦法1
假如你對c說話的庫函數比擬熟習的話,那末你第一個想到的確定是offsetof函數(其實只是個宏罷了,先權且如許叫著吧),我們man 3 offsetof檢查函數原型以下:
#include <stddef.h> size_t offsetof(type, member);
有了上述的庫函數,我們用一行代碼便可以弄定:
offsetof(struct node_t, c);
固然這並不是本文商量的重點,請持續浏覽。
辦法2
當我們對c說話的庫函數不熟習的時刻,此時也不要焦急,我們仍然可使用我們本身的辦法來處理成績。
最直接的思緒是:【構造體成員變量c的地址】 減去 【構造體肇端地址】
我們先來界說一個構造體變量node:
struct node_t node;
接著來盤算成員變量c的偏移量:
(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的思緒和上述的小成績,是否是很快就獲得了上面的聯系關系:
(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。
注:此處只是應用了編譯器的特征來盤算構造體偏移,並未對內存地址0有任何操作,有些同窗對此能夠還有些疑問,具體的懂得該成績可參考關於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)
示例2
和示例1一樣,我們先界說需求以下:
已知構造體類型界說以下:
struct node_t{ char a; int b; int c; };
int *p_c,該指針指向struct node_t x的成員變量c
構造體1Byte對齊
#pragma pack(1)
求:
構造體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拜訪該構造體的隨意率性成員變量了。
由此我們獲得盤算構造體肇端地址p_node的思緒為:
【成員變量c的地址p_c】減去【c在構造體中的偏移】
由示例1,我們獲得構造體struct node_t中成員變量c的偏移為:
(unsigned long)&(((struct node_t *)0)->c)
所以我們獲得構造體的肇端地址指針p_node為:
(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
我們異樣將上述代碼的功效界說為以下宏:
#define STRUCT_ENTRY(ptr, type, member) (type *)((unsigned long)(ptr)-OFFSET_OF(type, member)) 該宏的功效是經由過程構造體隨意率性成員變量的指針來取得指向該構造體的指針。我們應用下面的宏來修正之前的代碼以下:
STRUCT_ENTRY(p_c, struct node_t, c)
p_c為指向構造體struct node_t成員變量c的指針;
struct node_t構造體類型;
c為p_c指向的成員變量;
注:
上述示例中關於地址運算的一些解釋:
int a = 10; int * p_a = &a;
設
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
從上述三種情形,信任你應當能領會到我所要表達的意思了。(注:後續某博文將從編譯器的角度對該成績停止具體的論述)
結論
本文經由過程幾個示例描寫了c說話構造體有關偏移的一些成心思的工作,願望可以或許對你有所贊助。為何會有上述思慮,信任有些同窗曾經看出一些眉目,這也恰是後續博文將要描寫的主題。
如文中有毛病的地方,迎接指出。