所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協(xié)議湃鹊。轉(zhuǎn)載請(qǐng)注明來(lái)自 nixgnauhcuy's blog菇用!
如需轉(zhuǎn)載字柠,請(qǐng)標(biāo)明出處忌堂!
不要站著調(diào)試程序露戒,那會(huì)使得你的耐心減半护蝶,你需要的是全神貫注⌒氖—— Dave Storer
前言
記錄工作中學(xué)習(xí)到的知識(shí)酿矢,在這里做些筆記榨乎,方便自己后面經(jīng)常溫習(xí)。
正文
為什么要獲取結(jié)構(gòu)體成員變量的偏移
我在做嵌入式工作時(shí)瘫筐,在已知要獲取信息的 flash 地址時(shí)蜜暑,需要取出相對(duì)應(yīng)的信息元素,這個(gè)時(shí)候時(shí)常需要知道結(jié)構(gòu)體相對(duì)于已知地址的偏移策肝,方便快捷的從 flash 中取出信息元素肛捍,所以時(shí)常使用這個(gè)方式。
方法
我們先用普通的方法獲取結(jié)構(gòu)體偏移之众,代碼如下:
#include <stdio.h>
typedef struct {
int a;
int b;
int c;
}x_t;
void test(void)
{
x_t p;
printf("p_addr=%d\r\n", (int)&p);
printf("p.a_addr=%d\r\n", (int)&p.a);
printf("p.b_addr=%d\r\n", (int)&p.b);
printf("p.c_addr=%d\r\n", (int)&p.c);
printf("a_offset=%d\r\n", (int)&(p.a)-(int)&p);
printf("b_offset=%d\r\n", (int)&(p.b)-(int)&p);
printf("c_offset=%d\r\n", (int)&(p.c)-(int)&p);
}
int main(void)
{
test();
return 0;
}
輸出結(jié)果:
可以看出篇梭,如果要獲取結(jié)構(gòu)體成員變量相對(duì)于結(jié)構(gòu)體的偏移,則需要先獲取結(jié)構(gòu)體地址酝枢,再獲取成員變量地址,成員變量地址減去結(jié)構(gòu)體地址悍手,才能獲取相應(yīng)的偏移帘睦。
我們?cè)儆昧硪环N方式獲取結(jié)構(gòu)體偏移,代碼如下:
#include <stdio.h>
typedef struct {
int a;
int b;
int c;
}x_t;
void test1(void)
{
x_t * p = 0;
printf("a_offset = %d\n", (int)(&(p->a)));
printf("b_offset = %d\n", (int)(&(p->b)));
printf("c_offset = %d\n", (int)(&(p->c)));
}
int main(void)
{
test1();
return 0;
}
輸出結(jié)果:
這里先把結(jié)構(gòu)體地址指向地址 0x00000000坦康,這時(shí)候獲取成員變量相對(duì)于結(jié)構(gòu)體的偏移就輕松多了竣付,減少了一步操作,減少了計(jì)算量滞欠。
linux內(nèi)核代碼求結(jié)構(gòu)體成員變量偏移的方法
在內(nèi)核代碼 ./include/linux/stddef.h 文件中有如下定義:
#ifndef __CHECKER__
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
我們參考代碼編寫(xiě)后:
#include <stdio.h>
typedef struct
{
int a;
int b;
int c;
}x_t;
#define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE*)0)->MEMBER))
void test2()
{
printf("a_offset = %d\n", (int)&((x_t*)0)->a);
printf("b_offset = %d\n", (int)&((x_t*)0)->b);
printf("c_offset = %d\n", (int)&((x_t*)0)->c);
}
void test3()
{
printf("a_offset = %d\n", offsetof(x_t, a));
printf("b_offset = %d\n", offsetof(x_t, b));
printf("c_offset = %d\n", offsetof(x_t, c));
}
int main(void)
{
printf("test2():\r\n");
test2();
printf("test3():\r\n");
test3();
return 0;
}
輸出結(jié)果:
test2 和 test3 與上邊的 test1 方法其實(shí)是一樣的古胆,宏定義中的(TYPE*)0是一個(gè)空指針,如果使用空指針訪問(wèn)成員肯定造成段錯(cuò)誤筛璧,但是前面的 "&" 這個(gè)符號(hào)逸绎,表示我們僅僅取 MEMBER 字段的地址,而不是引用該字段內(nèi)容夭谤,因此不會(huì)造成段錯(cuò)誤棺牧。通過(guò)將結(jié)構(gòu)體地址指向 0x0 來(lái)獲得結(jié)構(gòu)體成員變量相對(duì)結(jié)構(gòu)體地址,即方便我們使用朗儒,也方便理解颊乘。
結(jié)尾
正如前言說(shuō)的,記錄工作中積累的點(diǎn)滴經(jīng)驗(yàn)醉锄,怕自己因?yàn)樯儆猛朔η模谶@里做個(gè)記錄,方便回顧恳不。