所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nixgnauhcuy's blog!
如需转载,请标明出处!
不要站着调试程序,那会使得你的耐心减半,你需要的是全神贯注。—— Dave Storer
前言
记录工作中学习到的知识,在这里做些笔记,方便自己后面经常温习。
正文
为什么要获取结构体成员变量的偏移
我在做嵌入式工作时,在已知要获取信息的 flash 地址时,需要取出相对应的信息元素,这个时候时常需要知道结构体相对于已知地址的偏移,方便快捷的从 flash 中取出信息元素,所以时常使用这个方式。
方法
我们先用普通的方法获取结构体偏移,代码如下:
#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;
}
输出结果:
可以看出,如果要获取结构体成员变量相对于结构体的偏移,则需要先获取结构体地址,再获取成员变量地址,成员变量地址减去结构体地址,才能获取相应的偏移。
我们再用另一种方式获取结构体偏移,代码如下:
#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 文件中有如下定义:
#ifndef __CHECKER__
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
我们参考代码编写后:
#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 来获得结构体成员变量相对结构体地址,即方便我们使用,也方便理解。
结尾
正如前言说的,记录工作中积累的点滴经验,怕自己因为少用忘了,在这里做个记录,方便回顾。