关于U-Boot的问题

找到一切的理由,您将了解很多。


最近,在SPI实现的一部分中查看了U-Boot代码,我遇到了一个宏来绕过可用设备的列表,经过几次转换后,我将其重置为container_of宏。 宏文本本身存在,并且令我有些惊讶的是,它与以前看到的版本有些不同。 进行了一次小型调查,得出了有趣的结果。

宏本身早已为人所知,并解决了一个有点奇怪的问题:我们有一个指向某个结构的字段(ptr)的指针,我们知道结构的类型(type)和字段的名称(member),我们需要获得一个指向整个结构的指针。 我不太了解如何出现这样的任务,也许作者“想要一些奇怪的东西”,但是一旦确定了任务,就需要解决它。 该解决方案是众所周知的:

#define container_of(ptr, type, member) \ ((type *)((char *)(ptr)-offsetof(type,member))) 

一切都是透明和清晰的,但是发现的实现有些复杂:

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

第二行与第一个解决方案几乎相同,但是宏的第一行做什么? 不,它的作用是可以理解的:它创建一个指向结构字段的常量局部指针,并为其分配宏参数的值-指向该字段的指针。 但是,为什么要这样做尚不清楚。

关于第一行目的的一个显而易见的考虑是检查宏的第一个参数是否确实是指向样式中的结构字段的指针:

  int *x = (X) 

在这种情况下,其形式有些深奥:

 typeof( ((type *)0)->member ) *__mptr = (ptr) 

因为验证所需的类型必须动态构建。

好的,让我们同意,这很有用,但是,我不得不在第二行中使用结果指针以避免编译器警告。

但是为什么要使用常量修饰符-即使没有此添加,宏也应该可以正常工作(我立即尝试并成功)。 作者不能无意间提出这一点。

没必要去那里看
我必须承认,它们提示了我答案,但我本人并没有猜到。

事实证明,如果我们试图找出常量结构的地址,则仅需要该修饰符,因为在这种情况下,编译器将需要invalid conversion from 'const xxx*' to `xxx*'

有趣的是,普通结构中字段本身的恒定性不仅不需要在宏中强制转换,还消除了对常量结构的需要,即,像这样的表达式:

 struct s1 { int data; const int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

在宏文本和表达式中编译时没有错误且没有修饰符:

 struct s1 { int data; int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

这是必需的。

对于那些对哈伯语非常了解的读者,我的发现似乎是“谢谢,队长”的风格,这很荒谬,但是对于其他人来说,它们可能很有趣。

Source: https://habr.com/ru/post/zh-CN466905/


All Articles