經過 struct 成員地址 獲取 struct 結構體地址

1. 問題描述:api

  如今定義了一個結構體:數據結構

  struct Foo函數

  {spa

    int a;指針

    int b;code

 

  };blog

  Foo foo;內存

  假如因爲函數傳參等緣由,如今程序只能拿到 foo.b 的地址,這時想經過某種方法獲取到 foo 結構體裏的其餘成員。io

  那麼問題來了,這就是如下主要討論的內容。class

2. 原理概述

  將地址 0 強制轉換成一個結構體指針,僞代碼:  struct foo *p = (struct Foo *)0;

  從而經過已知的結構體成員的地址減去結構體的首地址獲得已知結構體成員的內存偏移 , 僞代碼 : offset = &(p->b) - p;

  那麼問題就簡單了, 如今已知 foo.b 的地址,將其減去偏移便可獲得該結構體的首地址。

3. 實現舉例

 

//file name : list_head.c
#include <stdio.h>

struct list_head
{
    struct list_head *next;
};

struct fox
{
    unsigned int tail_color;
    unsigned int tail_length;
    struct list_head list;
};

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER )

#define container_of(ptr, type, member) ({\
            const typeof( ((type *)0)->member)* __mptr = (ptr);\
            (type *)((char*)__mptr - offsetof(type, member));}) 
int main(int argc, char** argv)
{
    unsigned short offset = 0;
    struct fox *p = 0;
    struct fox red_fox;
    red_fox.tail_color = 45;
    red_fox.tail_length = 10;
    unsigned int *p_t;

    printf("red_fox_addr: %x\n", &red_fox);
    printf("tail_color_addr: %x\n", &(red_fox.tail_color));
    printf("tail_length_addr: %x\n", &(red_fox.tail_length));
    printf("list_addr: %x\n", &(red_fox.list));

//    offset = (unsigned char*)&(p->list) - (unsigned char*)p;    
    offset = offsetof(struct fox, list);
    printf("offset: %d \n", offset);
    
    p_t = container_of(&red_fox.list, struct fox, list);
    printf("red_fox_addr: %x\n", p_t);
    printf("tail_color: %d \n", ((struct fox *)p_t)->tail_color);
    printf("tail_length: %d \n", ((struct fox *)p_t)->tail_length);

    return 0;
}

 

4.  應用

  Linux 中數據結構單鏈表使用的這種方法。好處也是顯而易見的,當用戶想經過單鏈表實現本身封裝的數據結構時不須要在單獨結構體定義單鏈表遍歷的指針和相關函數,僅僅實現包含 list_head 這個結構體成員便可。而內核提供了完整且高效的用於單鏈表操做 api.

 

做者能力有限,如發現錯誤歡迎指正。

相關文章
相關標籤/搜索