老生常谈的两个宏(Linux)
1、Linux内核中常用的两个宏定义
2、offsetof原理剖析
编译器做了什么?
- offsetof用于计算TYPE结构体中MEMBER成员的偏移位置
- 编译器清楚的知道结构体成员变量的偏移位置
- 通过结构体变量首地址与偏移量定位成员变量
3、编程实验
offsetof原理剖析 offsetof.c
#include <stdio.h>
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER) //根本不会访问0地址处,只是做加法
#endif
struct ST
{ //偏移位置
int i; // 0
int j; // 4
char c; // 8
};
void func(struct ST* pst)
{
int* pi = &(pst->i); // (unsigned int)&pst + 0 编译器的做法
int* pj = &(pst->j); // (unsigned int)&pst + 4
char* pc = &(pst->c); // (unsigned int)&pst + 8
//( size_t )&((TYPE*)0)->MEMBER
printf("pst = %p\n", pst);
printf("pi = %p\n", pi);
printf("pj = %p\n", pj);
printf("pc = %p\n", pc);
}
int main()
{
struct ST s = {0};
func(&s);
func(NULL);
printf("offset i: %d\n", offsetof(struct ST, i));
printf("offset j: %d\n", offsetof(struct ST, j));
printf("offset c: %d\n", offsetof(struct ST, c));
return 0;
}
4、container_of剖析
( { } ) 是何方神圣?
- ( { } )是GNU C编译器的语法扩展
- ( { } ) 与逗号表达式类似,结果为最后—个语句的值
typeof是—个关键字吗?
- typeof是GNU C编译器的特有关键字
- typeof只在编译期生效,用于得到变量的类型
最后的原理
container_of就是通过结构变量中一个成员的地址找到这个结构体变量的首地址。
5、编程实验
container_of原理剖析 container_of.c
#include <stdio.h>
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type*)0)->member)* __mptr = (ptr); \
(type*)((char*)__mptr - offsetof(type, member)); })
#endif
/*改写container_of_new和container_of结果一致*/
#ifndef container_of_new
#define container_of_new(ptr, type, member) ((type*)((char*)(ptr) - offsetof(type, member)))
#endif
/* const typeof(((type*)0)->member)* __mptr = (ptr)的意义:
* 做类型检查,在编译期编译器已知道member类型,不会在运行期访问0地址
*/
struct ST
{
int i; // 0
int j; // 4
char c; // 8
};
int main()
{
struct ST s = {0};
char* pc = &s.c;
int e = 0;
int* pe = &e;
//通过pc指针以及偏移量计算结构体变量s地址
struct ST* pst = container_of(pc, struct ST, c); //warning: use of GNU statement expression extension
printf("&s = %p\n", &s);
printf("pst = %p\n", pst);
return 0;
}
6、小结
编译器清楚的知道结构体成员变量的偏移位置
( { } )与逗号表达式类似,结果为最后—个语句的值
typeof只在编译期生效,用于得到变量的类型
container_of使用( { } ) 进行类型安全检查