#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
offsetof 用于计算 TYPE 结构体中 MEMBER 成员的偏移位置
#ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER) #endif
- 编译器清楚的知道结构体成员的偏移位置
- 经过结构体变量首地址与偏移量定位成员变量
#include <stdio.h> #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER) #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 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); printf("---------\n"); func(NULL); printf("---------\n"); 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; }
计算成员变量与其结构体变量首地址的偏移量编程
- ({}) 是 GNU 编译器的语法扩展
- ({}) 与逗号表达时相似,结果为最后一个语句的值
#include <stdio.h> void method_1() { int a = 0; int b = 0; int r = ( a = 1, b = 2, a + b ); printf("r = %d\n", r); } void method_2() { int r = ({ int a = 1; int b = 2; a + b; }); printf("r = %d\n", r); } int main() { method_1(); method_2(); return 0; }
输出:安全
r = 3 r = 3
- typeof 是 GUN C 编译器的特有关键字
- typeof 只在编译期生效,用于获得变量的类型
#include <stdio.h> int main() { int i = 100; typeof(i) j = i; const typeof(j) *p = &j; printf("sizeof(j) = %d\n", sizeof(j)); printf("j = %d\n", j); printf("*p = %d\n", *p); return 0; }
输出:测试
sizeof(j) = 4 j = 100 *p = 100
数学关系:spa
pc = p + offset
==>指针
size_t offset = offsetof(struct ST, c); struct ST *p = (struct ST*)((char*)pc - offset);
#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 struct ST { int i; int j; char c; }; int main() { struct ST s = {0}; char *pc = &s.c; struct ST *pst = container_of(pc, struct ST, c); printf("&s = %p\n", &s); printf("pst = %p\n", pst); return 0; }
输出:code
&s = 0061FEB8 pst = 0061FEB8
根据成员变量地址推导结构体变量首地址blog
思考:
const typeof(((type*)0)->member)* __mptr = (ptr);
下面这一段代码测试证实没有这一行也能够获得正确的结果,那它到底有什么做用呢?编译器
#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 #ifndef container_of_new #define container_of_new(ptr, type, member) ((type*)((char*)ptr - offsetof(type, member))) #endif struct ST { int i; int j; char c; }; int main() { struct ST s = {0}; char *pc = &s.c; struct ST *pst = container_of_new(pc, struct ST, c); // 注意!!! printf("&s = %p\n", &s); printf("pst = %p\n", pst); return 0; }
输出:数学
&s = 0061FEBC pst = 0061FEBC
答疑 1: 为了可以提供微弱但很重要的类型安全检查
由于 continer_of 只能用宏来实现,但宏是由预处理器处理,仅进行简单的文本替换,不会进行任何的类型检查。这就有可能致使在编写代码时,因为疏忽传递了错误的类型指针而编译器不发出任何警告。it
测试:
int main() { struct ST s = {0}; int e = 0; int *pe = &e; struct ST *pst = container_of_new(pe, struct ST, c); printf("&s = %p\n", &s); printf("pst = %p\n", pst); return 0; }
输出:【编译无错误,无警告,但运行结果错误】
&s = 0061FEBC pst = 0061FEB0
int main() { struct ST s = {0}; int e = 0; int *pe = &e; struct ST *pst = container_of(pe, struct ST, c); printf("&s = %p\n", &s); printf("pst = %p\n", pst); return 0; }
编译输出:
warning: initialization from incompatible pointer type [-Wincompatible-pointer-types] const typeof(((type*)0)->member)* __mptr = (ptr); \ ^
答疑 2: 为何使用 ({})
当定义指针变量时,一行代码再也没法完成 continer_of 的总体功能(逗号表达式中不能够定义变量),因而使用了 GNU 的扩展语法({})分多行定义局部变量。
答疑 3: 此行代码是怎样获取成员变量的类型的呢?
typeof(((type*)0)->member)
- 编译器清楚的知道结构体成员变量的偏移位置
- ({}) 与逗号表达式类型,结果为最后一个语句的值
- typeof 只在编译期生效,用于获得变量的类型
- container_of 使用 ({}) 进行类型安全检查
以上内容整理于狄泰软件学院系列课程,请你们保护原创!