list.h中宏list_entry宏的解析与应用

list_enty这个宏算是内核里面比较复杂的宏了。下面就对它的结构进行解析。宏的结构为:

 

这个宏的作用是通过ptr指针获取type的地址,其中ptr是member的指针,member是type结构的成员。

这个宏里面包含另外一个宏,container_of。这个宏位于/linux/kerner.h,它的结构为:

 

这个宏包含两条语句。第一条:const typeof( ((type *)0)->member ) *__mptr = (ptr);首先将0转化成type类型的指针变量(这个指针变量的地址为0×0),然后再引用member成员(对应就是((type *)0)->member ))。注意这里的typeof(x),是返回x的数据类型,那么 typeof( ((type *)0)->member )其实就是返回member成员的数据类型。那么这条语句整体就是将__mptr强制转换成member成员的数据类型,再将ptr的赋给它(ptr本身就是指向member的指针)。第二条语句里面的offsetof也是一个宏,它在/linux/stddef.h中这样定义:

 

((TYPE *)0)->MEMBER)这个其实就是提取TYPE类型中的member成员,那么&((TYPE *)0)->MEMBER)得到member成员的地址,再强制转换成size_t类型(unsigned int)。因为它的结构体地址从0开始定义,所以得到的member成员的地址就是它相对于结构体的偏移地址。再回到contanier_of的第二句话,把得到的member的地址强制转化成char *,(这里不是很理解,借用别人的思路),因为如果member是非char型的变量,比如为int型,并且假设返回值为offset,那么这样直接减去偏移量,实际上__mptr会减去sizeof(int)*offset!这和指针的自加自减运算一样,根据其指向的变量的类型不同而不同。(同意)

接下来,container_of宏的含义就不言而喻了,也就是list_entry宏。

下面是对该宏实现的一个应用实例,以加深理解。

 

运行结果如下:

 

$ ./offset 

address of &stu is=0xbf89bb18

 

addressx of &stu-num=0xbf89bb18 offset of stu->num is=0

 

addressx of &stu-name=0xbf89bb1c offset of stu->name is=4

 

addressx of &stu-sex=0xbf89bb1d offset of stu->sex is=5

 

computing address of &stu using:

address and offset of stu->num=0xbf89bb14

address and offset of stu->name=0xbf89bb17

address and offset of stu->sex=0xbf89bb18

 

 

 

 

 

 

 

### C/C++ 中函数 `__VA_ARGS__` 的使用 #### 定义可变参数 在C99和C++11之后的标准中,引入了用于定义带有可变数量参数的的能力。这通过特殊的标记 `...` 和预处理器变量 `__VA_ARGS__` 来实现。 对于简单的场景,可以直接利用 `__VA_ARGS__` 将剩余的所有参数传递给目标函数: ```c #define LOG_INFO(fmt, ...) fprintf(stdout, fmt "\n", __VA_ARGS__) ``` 此接受一个格式字符串作为第一个参数,随后跟随任意数目的额外参数,这些参数会被打包成 `__VA_ARGS__` 并最终传入到 `fprintf` 函数里[^1]。 当希望处理可能为空的情况时,在某些编译器环境下(如GCC),可以在定义中加入双重井号运算符 (`##`) 前缀于 `__VA_ARGS__` 或者其他参数名前,以便移除多余的逗号: ```c #define ERROR_MSG(fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__) ``` 上述例子展示了如何创建一个错误消息打印,其中如果调用时不提供任何附加参数,则不会因为多出来的逗号而引发编译错误[^3]。 #### 应用实例 下面给出几个实际的应用案例来展示不同方式下 `__VAARGS__` 的运用: ##### 示例一:基本日志记录功能 ```c #include <stdio.h> // 日志信息输出 #define LOG(msg, ...) printf("[INFO] %s():%d - " msg "\n", __FUNCTION__, __LINE__, __VA_ARGS__) int main() { int value = 42; LOG("The answer is %d.", value); return 0; } ``` 这段代码会输出当前执行位置以及指定的信息至控制台,并且能够正确解析并显示所有传递过来的参数值[^2]。 ##### 示例二:动态对象初始化辅助工具 ```cpp template<typename T> T* createInstance(const char* name, ...) { va_list args; va_start(args, name); // 这里的NEW是一个自定义,用来简化new操作的同时支持更多参数输入 #define NEW(T, ...) new T(__VA_ARGS__) T* instance = NEW(T, args); va_end(args); return instance; } struct Point { double x,y,z; }; int main(){ auto p = createInstance<Point>("Point Instance Name", 1.0, 2.0, 3.0); // ... delete p; } ``` 这里演示了一个模板化的工厂模式样式的函数,它可以根据不同的类型创建相应的实例,并允许用户向构造函数传递多个初始值。注意这里的 `NEW` 是如何工作的——它可以接收不定数量的实际参数并通过 `__VA_ARGS__` 转交给类的构造函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值