offsetof與container_of宏分析

offsetof宏:結構體成員相對結構體的偏移位置 container_of:根據結構體成員的地址來獲取結構體的地址html

offsetof 宏

原型:linux

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

(TYPE *)0很是巧妙,告訴編譯器有一個指向結構體 TYPE 的指針,其地址是0,而後取該指針的 MEMBER 地址 &((TYPE *)0)->MEMBER,由於基址是0,因此這時獲取到的 MEMBER 的地址就是至關於在結構體 TYPE 中的偏移量了。 Example:post

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>

struct TYPE{
    int mem;
    int member;
};

int main()
{
    struct TYPE type;
    printf("&type = %p\n", &type);
    printf("&type.member = %p\n", &type.member);
    printf("&((struct type *)0)->member = %lu\n", ((size_t)&((struct TYPE *)0)->member) );
    printf("offsetof(struct TYPE member) = %zd\n", offsetof(struct TYPE, member));
    return 0;
}
/*
result:
&type = 0x7ffc1104a110
&type.member = 0x7ffc1104a114
&((struct type *)0)->member = 4
offsetof(struct TYPE member) = 4
*/

container_of 宏

原型:linux-4.18.5this

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({				\
	void *__mptr = (void *)(ptr);					\
	BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&	\
			 !__same_type(*(ptr), void),			\
			 "pointer type mismatch in container_of()");	\
	((type *)(__mptr - offsetof(type, member))); })

網上所見更可能是底下這個版本:spa

#define container_of(ptr, type, member) ({      \   
 const typeof( ((type *)0)->member ) *__mptr = (ptr);    \  
  (type *)( (char *)__mptr - offsetof(type,member) );})

第一部分:void *__mptr = (void *)(ptr); const typeof( ((type *)0)->member ) *__mptr = (ptr); 兩個的差異在於 __mptr 的類型一個是 void * ,一個是 type *。 void * 較爲容易理解,下面來看看 type *: 關於 typeof 關鍵字其做用是返回變量的類型,簡單理解就是以下,詳細可參見GCC typeof在kernel中的使用——C語言的「編譯時多態」指針

int a;
typeof(a) b; //這等同於int b;
typeof(&a) c; //這等同於int* c;

所以const typeof( ((type *)0)->member ) *__mptr = (ptr); 的做用就是經過 typeof 獲取結構體成員 member 的類型,而後定義一個這個類型的指針變量 __mptr 並將其賦值爲 ptr。 第二部分:(type *)( (char *)__mptr - offsetof(type,member) ),經過offsetof宏計算出 member 在 type 中的偏移,而後用 member 的實際地址__mptr減去偏移,獲得 type 的起始地址。從上面關於offsetof宏的 Example 也能夠驗證這一點: &type.member = 0x7ffc1104a114 - &((struct type *)0)->member = 4 = &type = 0x7ffc1104a110code

相關文章
相關標籤/搜索