获取结构体成员偏移量的方法
发表于更新于
字数总计:863阅读时长:3分钟 深圳
嵌入式嵌入式C获取结构体成员偏移量的方法
nixgnauhcuy
不要站着调试程序,那会使得你的耐心减半,你需要的是全神贯注。—— Dave Storer
前言
记录工作中学习到的知识,在这里做些笔记,方便自己后面经常温习。
正文
为什么要获取结构体成员变量的偏移
我在做嵌入式工作时,在已知要获取信息的 flash 地址时,需要取出相对应的信息元素,这个时候时常需要知道结构体相对于已知地址的偏移,方便快捷的从 flash 中取出信息元素,所以时常使用这个方式。
方法
我们先用普通的方法获取结构体偏移,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #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; }
|
输出结果:
可以看出,如果要获取结构体成员变量相对于结构体的偏移,则需要先获取结构体地址,再获取成员变量地址,成员变量地址减去结构体地址,才能获取相应的偏移。
我们再用另一种方式获取结构体偏移,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #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; }
|
输出结果:
这里先把结构体地址指向地址 0x00000000,这时候获取成员变量相对于结构体的偏移就轻松多了,减少了一步操作,减少了计算量。
linux内核代码求结构体成员变量偏移的方法
在内核代码 ./include/linux/stddef.h 文件中有如下定义:
1 2 3 4
| #ifndef __CHECKER__ #undef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif
|
我们参考代码编写后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #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; }
|
输出结果:
test2 和 test3 与上边的 test1 方法其实是一样的,宏定义中的(TYPE*)0是一个空指针,如果使用空指针访问成员肯定造成段错误,但是前面的 “&” 这个符号,表示我们仅仅取 MEMBER 字段的地址,而不是引用该字段内容,因此不会造成段错误。通过将结构体地址指向 0x0 来获得结构体成员变量相对结构体地址,即方便我们使用,也方便理解。
结尾
正如前言说的,记录工作中积累的点滴经验,怕自己因为少用忘了,在这里做个记录,方便回顾。