其他分享
首页 > 其他分享> > 由已知结构体里的某个成员名及该成员地址获取该结构体指针

由已知结构体里的某个成员名及该成员地址获取该结构体指针

作者:互联网

最近在学习数据结构链表,发现在可复用的链表实现中一般仅有一个指针域结点,并没有数据域结点,链表的常用操作也只涉及next域。只是在使用链表时候会再定义一个数据元素结构体,结构体里会包含链表元素,这种操作搞的有点懵,为啥链表常用接口都没有数据的相关操作还可以使用链表,后面仔细想了一下应该只是借助链表来存储了结点的各个地址,而链表是结构体的一个成员,借助结构体成员的地址和偏移地址,可以求得结构体的首地址,然后就可以引用结构体来读取和配置真正的数据信息。

以下是转载的https://blog.csdn.net/shengnan_wu/article/details/8267741,文章总体上符合上面介绍的思路,但是具体例子介绍的不太详细,我这里进一步补充了以下。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在很多操作系统里面几乎都会用到链表 , 一般的链表的节点(结构体)都维护着一个list(结构体) , 如果是单向链表 , 则list里面只有一个元素next指针 , 指向下一个节点的list .如果是双向链表 , 则list里面由两个元素:previous指针和next指针 ,其中previous指向上一个节点的list , next指向下一个节点的list .

下面我画出了双向链表的典型结构,这里要仔细说明下,list才是链表的结构体,Node其实不是链表的结点了,已经是在具体使用链表的时候定义的数据结构体了,数据结构体中会包含list链表成员

由以上的结构可以发现组成链表的结构使用的node里面的list  .那问题就出现了 , 如何才能从获得的list结构来得到相应的node指针呢 

这里我们先做一些声明 , 下面的讨论都用listAddr来表示list的地址 , nodeAddr来表示node的地址,这里nodeAddr可以理解成数据结构体的首地址,listAddr可以看成数据结构体的成员地址,成员地址=首地址+偏移量

  1)既然我们知道了listAddr了, 如果要得到对应的nodeAddr,  很简单的公式如下:

listAddr= nodeAddr + offset      ==>      nodeAddr = listAddr - offset  ;

  2)其中 listAddr 已知 ,  我们只需要求出offset 就行了;

  3)offset 如何求呢  ?   linux里面有关于获得这个offset的方法 , 也是很多程序都采用的方法 . 这里也根据linux里面的方法来分析;

        记得以前的上数学课的时候 , 老师跟我们说过 , 在做选择题的时候某些题是可以直接把一些变量用简单的常数代进去求解得到我们想要的结果 . 这个方法其实也可以用在这里 . 我们知道一个已知结构体的起始地址跟该结构体里面的某个成员的偏移量是不会变的 , 即 offset是个常数 . 也就是说无论node地址取什么值 ,offset都是不变 . 这样我们就可以用一个常数强制转换为node指针(这样nodeAddr地址就知道了) , 然后根据listAddr-nodeAddr 推出 offset .

   4)offset知道了 , 由listAdd - offset 得真正的nodeAddr .

以下程序及打印结果验证了这个思路是可行的:


打印结构如下:

既然验证了这个思路是可行的 , 那么就得来设计一个已知结构体类型 ,其中一个元素名称,该元素的地址  的函数或者宏定义 . 但是函数实现太不现实了 , 我想不出有什么办法可以通过以上3个已知条件来返回这个类型的结构体指针 . 所以只能通过宏定义来实现了 .

 宏定义的实现:

    1>宏定义名称  :   getStructAddr(struct_type , member_name ,member_ptr) 

        其中struct_type 是结构体类型 ; member_name 是成员名称 ; member_ptr 是成员地址 .

    2>宏内容  (可以有两种形式,如下):

             a.使用({})形式的宏内容 , 这种方式可以直接在宏调用时作为赋值操作的右值 , 但想要的返回值必须是最后一条语句的运行结果 . 

实现如下;


打印结果如下:

 
    

b.使用do{...}while(0)形式的宏内容 , 这种方式不可以在宏调用时作为赋值操作的右值 , 所以只能通过给宏多加一个参数用于存放需要返回的值 .

实现如下:


打印结果如下:

标签:成员,offset,list,名及,链表,地址,体里,nodeAddr,结构
来源: https://blog.csdn.net/lanhuazui10/article/details/117405651