博客首页 注册 建议与交流 排行榜 加入友情链接
推荐 投诉 搜索: 帮助

哥德巴赫猜想

达则兼济天下,穷则独善其身。
  Godbach.cublog.cn

关于作者
姓名:Godbach
职业:
年龄:
位置:
个性介绍:A loser!
|| << >> ||
我的分类


通过结构体某个成员的地址计算结构体首地址
本文欢迎自由转载,但保持本文的完整性,并注明出处。
 
最近在CU论坛上有很多人在问这样一个问题:给出一个结构体成员的地址计算该结构体的起始地址。其实这个题我之前也没有接触过,据说内核代码中有这样用的,但还没有看到。不过觉得这个题的解决方法还是有一定技巧的,就总结一下。下面是实现的代码。
 

/*

Author: Godbach

Date: Oct 23, 2008

*/

#include <stdio.h>
#define STRUCT_OFFSET(stru_name, element) (unsigned long)&((struct stru_name*)0)->element
struct stru_addr
{
    int a;
    char b;
    int d;
    char c;

};

int main(void)
{
    struct stru_addr s;
    printf("start addr of s = %x\n", &s.a);
    
    unsigned long offset = STRUCT_OFFSET(stru_addr, c);

    printf("c_addr = %x, offset = %u\n", &s.c, offset);
    printf("start addr of s caculated from c addr: %x\n", (char *)&s.c - offset);
    return 0;
}

 

其实整个程序中最关键的部分就是如何求出结构体中某个成员相对于结构体首地址的偏移量。这里的解决方法是:假设存在一个虚拟地址0,将该地址强制转换成为该结构体指针类型(struct stru_name*)0。那么地址0开始到sizeof(struct)-1长度的内存区域就可以视为一个结构体的内存。这样结构体中任何一个元素都可以通过对该结构体指针解引用得到。由于该结构体的起始地址为0, 因此任何一个成员的地址应该等于其相对于结构体起始地址的偏移,这也就是计算偏移量的方法:(unsigned long)&((struct stru_name*)0)->element。

上面程序执行的结果如下:
[root@localhost tmp]# ./a.out
start addr of s = bf81b820
c_addr = bf81b82c, offset = 12
start addr of s caculated from c addr: bf81b820

上述的结果中还同时考虑了结构体内的对齐问题。

发表于: 2008-10-23,修改于: 2009-03-02 13:07,已浏览1361次,有评论5条 推荐 投诉


网友评论
网友: 本站网友 时间:2008-11-12 21:41:19 IP地址:60.28.145.★
offset没有定义在块头,gcc可以编译通过?
我的VC6不行,找了半天错....

网友: Godbach 时间:2008-11-13 09:55:51 IP地址:219.238.94.★
offset没有定义在块头
--------这句话是什么意思?

网友: brvman 时间:2009-03-01 19:23:31 IP地址:220.169.0.★
求偏移量的宏中(int)&((struct stru_name*)0)->element应该强制转换为unsigned long,Linux内核就是这样写的,转换为int不一定能得到正确的结果,因为强制转换为int后,这个值可能会变为负值,而且int可能为16位。

Blog作者的回复:
多谢brvman兄的指正。已做更改。
内核中确实用的unsigned long。这里应该使用无符号的。


网友: ddvv 时间:2009-03-27 09:46:15 IP地址:116.4.31.★
内核里面的list_entry宏就是这样搞的,第一件见到的时候觉得很神奇~

网友: Godbach 时间:2009-03-28 23:31:41 IP地址:60.29.151.★
呵呵,是啊。所以平时多看一下内核代码,对C的提高是很大的。

 发表评论