• linux内核中的offsetof、container_of、双链表list.h实践


    先直接上程序,代码中包含了注释已经说清楚。在linux的应用层中编译、测试:

    感谢李慧芹的B站课程:史上最强最细腻的linux嵌入式C语言学习教程【李慧芹老师】_哔哩哔哩_bilibili

    1. #include
    2. #include
    3. // 下面的宏来自于:
    4. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    5. #define container_of(ptr, type, member) ({ \
    6. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    7. (type *)( (char *)__mptr - offsetof(type,member) );})
    8. // 下面的结构体定义来自于
    9. struct list_head {
    10. struct list_head *next;
    11. struct list_head *prev;
    12. };
    13. // 下面的宏及函数摘自于
    14. #define list_entry(ptr, type, member) \
    15. container_of(ptr, type, member)
    16. #define list_for_each(pos, head) \
    17. for (pos = (head)->next; pos != (head); pos = pos->next)
    18. #define LIST_HEAD_INIT(name) { &(name), &(name) }
    19. #define LIST_HEAD(name) \
    20. struct list_head name = LIST_HEAD_INIT(name)
    21. void INIT_LIST_HEAD(struct list_head *list)
    22. {
    23. list->next = list;
    24. list->prev = list;
    25. }
    26. // 将 new 插入到 prev 和 next 的中间
    27. void __list_add(struct list_head *new,
    28. struct list_head *prev,
    29. struct list_head *next)
    30. {
    31. next->prev = new;
    32. new->next = next;
    33. new->prev = prev;
    34. prev->next = new;
    35. }
    36. // 将 new 插入到 head 的后面
    37. void list_add(struct list_head *new, struct list_head *head)
    38. {
    39. __list_add(new, head, head->next);
    40. }
    41. // 将 new 插入到 head 的前面
    42. void list_add_tail(struct list_head *new, struct list_head *head)
    43. {
    44. __list_add(new, head->prev, head);
    45. }
    46. void __list_del(struct list_head * prev, struct list_head * next)
    47. {
    48. next->prev = prev;
    49. prev->next = next;
    50. }
    51. void __list_del_entry(struct list_head *entry)
    52. {
    53. __list_del(entry->prev, entry->next);
    54. }
    55. //
    56. // 下面是业务层应用代码
    57. //
    58. // 应用层业务的结构体定义:
    59. struct student
    60. {
    61. int id;
    62. char name[128];
    63. int ch; //语文分数
    64. int ma; //数学分数
    65. int en; //英语分数
    66. struct list_head list; //包含一个 list_head
    67. };
    68. void print_stu(struct student *st);
    69. int main()
    70. {
    71. //
    72. // 0.1 测试宏 offsetof 使用
    73. //
    74. printf("id offset=%ld\n", offsetof(struct student, id));
    75. printf("name offset=%ld\n", offsetof(struct student, name));
    76. printf("ch offset=%ld\n", offsetof(struct student, ch));
    77. printf("ma offset=%ld\n", offsetof(struct student, ma));
    78. printf("en offset=%ld\n", offsetof(struct student, en));
    79. printf("list offset=%ld\n", offsetof(struct student, list));
    80. /*
    81. id offset=0
    82. name offset=4
    83. ch offset=132
    84. ma offset=136
    85. en offset=140
    86. list offset=144
    87. 上面看出,宏offsetof(TYPE, MEMBER),就是返回成员MEMBER相对首的偏移!
    88. */
    89. // 0.2 测试宏 container_of 使用
    90. struct student stu={100, "std100", 78, 88, 98, NULL,};
    91. struct student *p=container_of(&stu.list, struct student, list);
    92. printf("&stu=%p\n", &stu);
    93. printf("&stu.list=%p\n", &stu.list);
    94. printf("&p=%p\n", p);
    95. /*
    96. &stu= 0x7fffa2f4e1b0
    97. &stu.list= 0x7fffa2f4e240 0x240-0x1b0=144 即是上述list的偏移off
    98. &p= 0x7fffa2f4e1b0
    99. 上面看出,宏container_of(ptr, type, member) 即是返回结构体变量的首地址。
    100. 那么问题来了,为何搞这么复杂的一个转换来获取首地址呢?
    101. 直接使用&stu不就得到完了嘛!别急,看下面的应用!
    102. */
    103. int i=0;
    104. LIST_HEAD(head);
    105. //
    106. // 1. 创建5个结构体,使用 list 连起来
    107. //
    108. for(i=0; i<5; i++)
    109. {
    110. struct student *st=malloc(sizeof(struct student));
    111. sprintf(st->name, "stu%02d", i+1);
    112. st->id=i+1;
    113. st->ch=rand()%100;
    114. st->ma=rand()%100;
    115. st->en=rand()%100;
    116. printf("id=%d, name=%s, ch=%d, ma=%d, en=%d\n",
    117. st->id, st->name, st->ch, st->ma, st->en);
    118. list_add(&(st->list), &head); //这里每次插入到head的后面!
    119. }
    120. /*
    121. id=1, name=stu01, ch=83, ma=86, en=77
    122. id=2, name=stu02, ch=15, ma=93, en=35
    123. id=3, name=stu03, ch=86, ma=92, en=49
    124. id=4, name=stu04, ch=21, ma=62, en=27
    125. id=5, name=stu05, ch=90, ma=59, en=63
    126. 注意上述创建的原始顺序!
    127. */
    128. printf("\n");
    129. //
    130. // 2. 遍历打印
    131. //
    132. struct list_head *c;
    133. list_for_each(c, &head)
    134. {
    135. struct student *st=container_of(c, struct student, list);
    136. print_stu(st);
    137. }
    138. /*
    139. id=5, name=stu05, ch=90, ma=59, en=63
    140. id=4, name=stu04, ch=21, ma=62, en=27
    141. id=3, name=stu03, ch=86, ma=92, en=49
    142. id=2, name=stu02, ch=15, ma=93, en=35
    143. id=1, name=stu01, ch=83, ma=86, en=77
    144. 因为是每次插入到head的后面,所以链表里面的顺序是5、4、3....
    145. */
    146. //
    147. // 3. 查找一个节点
    148. //
    149. list_for_each(c, &head)
    150. {
    151. struct student *st=container_of(c, struct student, list);
    152. if(st->id==3)
    153. {
    154. printf("\nfind it!\n");
    155. print_stu(st);
    156. }
    157. }
    158. //
    159. // 4. 删除一个节点
    160. //
    161. list_for_each(c, &head)
    162. {
    163. struct student *st=container_of(c, struct student, list);
    164. if(st->id==3)
    165. {
    166. __list_del_entry(&st->list);
    167. free(st);
    168. }
    169. }
    170. //
    171. // 5. 再次输出打印
    172. //
    173. printf("\nreprintf:\n");
    174. list_for_each(c, &head)
    175. {
    176. struct student *st=container_of(c, struct student, list);
    177. print_stu(st);
    178. }
    179. }
    180. void print_stu(struct student *st)
    181. {
    182. printf("id=%d, name=%s, ch=%d, ma=%d, en=%d\n",
    183. st->id, st->name, st->ch, st->ma, st->en);
    184. }

  • 相关阅读:
    Qt开发:MAC安装qt、qtcreate(配置桌面应用开发环境)
    数据结构---HashMap和HashSet
    类和对象3:组合和混入
    websocket 封装在vue中使用
    Python 进阶:函数装饰器
    边缘计算那些事儿--网络切片技术(1)
    vue基础笔试题
    拼多多商品详情api接口
    【408考研】数据结构 —— 栈和队列的应用
    【数学计算】使用mathematica计算圆周率π
  • 原文地址:https://blog.csdn.net/dijkstar/article/details/133748389