You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

container_of宏两次传入api参数的原因及是否为多态行为解析

为什么container_of调用中两次传入"api"参数

两个位置的api语义完全无关,只是命名碰巧重合,和container_of宏本身的设计要求没有任何关系。

首先明确container_of的核心作用:给定结构体中某个成员的内存指针,反向计算出宿主结构体实例的首地址,它的三个传入参数的含义是固定的,我们可以从通用的标准实现看清楚逻辑:

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

三个参数分别对应:

  • 第一个参数ptr:结构体成员的运行时实际指针值
  • 第二个参数type:要获取的外层宿主结构体的类型名
  • 第三个参数member:目标成员在宿主结构体内定义时的字段名,用于编译期计算该成员相对于结构体首地址的内存偏移量

对应到你看到的调用container_of(api, api_p_t, api):

  • 第一个api是set函数的入参形参,类型为api_2*,是运行时传入的、指向某个api_p_t实例中api成员的内存地址,是真实的运行时值。
  • 第三个api是api_p_t结构体定义里的成员名(对应代码中typedef struct{ api_2 api; int value; } api_p_t;的字段定义),是仅用于编译期计算偏移的符号,和前面的形参没有任何绑定关系。

这个重名完全是代码作者的命名习惯导致的巧合:如果你把set函数的形参改名为p_api,宏调用就会变成container_of(p_api, api_p_t, api),根本不会出现重复的"api"参数。

这种实现是否属于多态行为

这是C语言实现运行时多态的经典惯用法,和C++/Java等面向对象语言中基于接口/虚函数的多态本质逻辑一致,只是没有编译器提供语法层封装,需要手动实现:

  • 代码中的struct api_1(typedef别名api_2)相当于公共接口基类,内部存储的函数指针api_set set就相当于面向对象语言中的虚方法,定义了所有实现类必须遵循的方法签名。
  • 类似api_p_t这种内嵌api_2成员的结构体,相当于实现了该接口的具体子类:内嵌的api_2成员会绑定当前子类对应的方法实现(比如这里绑定的是set函数),子类的私有数据(比如int value)存在结构体的其他字段,对外完全隐藏。
  • 上层逻辑只需要拿到api_2*类型的接口指针,不需要关心背后实际绑定的是api_p_t还是其他实现类,直接调用接口内的函数指针即可;方法内部通过container_of拿到具体子类的实例指针,操作子类的私有数据。不同子类可以给同一个接口绑定不同的实现函数,运行时会自动执行对应逻辑,完全符合多态"同一接口,不同实现"的核心定义。

你完全可以基于同一个接口扩展其他实现,上层调用逻辑不需要修改任何代码就能兼容:

// 另一个接口实现,内部存储float类型数据
typedef struct{
    api_2 api;
    float float_val;
} api_float_impl_t;

// 对应set方法的独立实现
void set_float(api_2 *api, int a){
    api_float_impl_t *priv = container_of(api, api_float_impl_t, api);
    priv->float_val = (float)a / 100;
}

内容的提问来源于stack exchange,提问作者Wojciech Mierzejewski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 10:12:26