&((type *)0)->member的用法

文章解释了rt_list_entry宏在rt-thread中的作用,即通过计算结构体成员的偏移量来获取结构体地址。作者还给出了C语言中结构体偏移的示例,并指出Linux内核链表同样采用这种技术实现灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 问题缘由,在学习 rt-thread 内核的时候遇到了这么一行代码:

to_thread = rt_list_entry(rt_thread_priority_table[0].next,
                          struct rt_thread,    
                          tlist);

而 rt_list_entry 的宏定义如下:

/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \ 
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

#define rt_list_entry(node, type, member) \ 
rt_container_of(node, type, member)

但是不理解 &((type *)0)->member 这段代码的意义,于是在网上搜集了一些资料后,总结如下:

该语句可以在不生成结构体实例的情况下计算结构体成员的偏移量,结构体变量的某成员的地址等于该结构体变量的基址加上结构体成员变量在结构体中的偏移量。而 (type*)0 就是假设地址 0 处存放的是一个 type 类型的结构体变量,这样的话这个结构体变量的基址就是 0,所以结构体成员 ((type *)0)->member 的地址的大小在数值上就等于该结构体成员在结构体中的偏移量

下面是一个简单的例子:

struct test {
    short a; // 偏移 0 个字节,占用 2 个字节
    short b; // 偏移 2 个字节,占用 2 个字节
    int c; // 偏移 4 个字节,占用 4 个字节
    int d; // 偏移 8 个字节,占用 4 个字节
};

int main(void) {

    printf("a 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->a, (unsigned long) sizeof(((struct test *)0)->a));
    printf("b 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->b, (unsigned long) sizeof(((struct test *)0)->b));
    printf("c 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->c, (unsigned long) sizeof(((struct test *)0)->c));
    printf("d 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->d, (unsigned long) sizeof(((struct test *)0)->d));
    return 0;
}

可以看到结果和我们预想的一样:

因为在 C 中,结构体内成员的偏移量是不会发生变化的,所以知道了一个结构体中的成员的地址和偏移量,我们就可以计算出结构体的地址,如下面这个例子:

struct test {
    short a; // 偏移 0 个字节,占用 2 个字节
    short b; // 偏移 2 个字节,占用 2 个字节
    int c; // 偏移 4 个字节,占用 4 个字节
    int d; // 偏移 8 个字节,占用 4 个字节
};

int main(void) {
    struct test t1;
    t1.a = 1;
    t1.b = 2;
    t1.c = 3;
    t1.d = 4;

    // 假设我们只知道 t1 结构体中 d 的地址
    int * d_ptr = &t1.d;
    // 那么我们可以通过如下方式获取到 t1 结构体的地址
    struct test *t2 = (struct test *)((char *)d_ptr - (unsigned long)(&((struct test *)0)->d));
    printf("a = %d, b = %d, c = %d, d = %d\n", t2->a, t2->b, t2->c, t2->d);
    return 0;
}

最终的结果如下:

在 rt-thread 中,链表结构体很简单,只有前向指针和后向指针两个参数:

如果有结构体需要使用链表结构的话,直接把上述结构体包含进来即可:

在遍历链表的时候,我们只需要用到 rt_list_t 结构体,找到对应的结构体后,我们再通过 rt_list_t 结构体及其偏移计算出 rt_thread 结构体的地址,是不是很灵活!

另外,在 linux 内核中,链表也是用这种方式实现的。 

<think>好的,我现在需要解决用户遇到的C/C++编译错误:“member reference base type 'rfc_sig_cfg_type *' is not a structure or union”。用户提到在代码中使用了类似`cfg->logical_device`这样的访问,但编译器报错。我需要分析可能的原因并提供解决方案。 首先,这个错误通常发生在试图通过指针访问结构体或联合体的成员时,但指针的类型并没有被正确声明为结构体或联合体。可能的情况有几个: 1. **指针类型未正确声明**:可能`rfc_sig_cfg_type`没有被正确定义为结构体或联合体,或者在头文件中没有被包含进来。例如,如果代码中使用了`typedef`但未完成结构体的定义,或者头文件缺失,编译器可能无法识别该类型的具体结构,导致访问成员时出错。 2. **指针未正确初始化或类型不匹配**:可能`cfg`指针实际指向的类型与声明的类型不一致。比如,如果`cfg`被声明为某种类型的指针,但实际上被赋值为另一种类型的地址,尝试访问成员时就会出错。 3. **作用域或可见性问题**:结构体的定义可能在一个特定的命名空间或作用域内,而使用的时候没有正确引用,导致编译器无法识别成员的存在。 接下来,我需要参考用户提供的引用内容。根据引用[1],用户之前处理过类似的问题,通过删除重复的管脚定义解决了问题。这可能意味着代码中存在重复定义或结构体成员冲突的情况。此外,引用[2]中的代码片段展示了结构体成员的赋值,如`asm_info->mfg_id = 0x349;`,这暗示正确的结构体访问方式。 可能的解决步骤包括: - 检查`rfc_sig_cfg_type`的定义是否存在,确保其确实是一个结构体或联合体,并且所有成员都正确声明。 - 确认包含该结构体定义的头文件是否被正确包含在代码中。 - 检查`cfg`指针的声明是否正确,是否与实际的类型匹配。 - 如果有多个同名的结构体定义,可能存在命名空间冲突,需要明确作用域。 另外,用户提供的代码中有`cfg->logical_device`这样的访问,如果`cfg`是指向指针的指针(例如`rfc_sig_cfg_type **cfg`),则需要先解引用,如`(*cfg)->logical_device`。或者,如果`cfg`本身是一个指针,但实际类型不是结构体,比如被错误地声明为某种基本类型指针,这也会导致错误。 还需要检查是否有拼写错误,比如结构体成员名称是否正确,或者是否在某个地方错误地使用了`.`操作符而不是`->`。 总结可能的解决方案: 1. 确认结构体的定义和包含。 2. 检查指针的声明和解引用是否正确。 3. 查找可能的类型不匹配或重复定义。</think>要解决C/C++编译错误`member reference base type 'rfc_sig_cfg_type *' is not a structure or union`,需按以下步骤排查: ### 1. 确认结构体定义 首先检查`rfc_sig_cfg_type`是否正确定义为**结构体或联合体**: ```c // 示例:正确的结构体定义 typedef struct { int logical_device; int band; // 其他成员... } rfc_sig_cfg_type; ``` - **问题场景**:若未定义或定义为其他类型(如枚举或基本类型),编译器会报此错误。 - **解决方法**:确保结构体定义存在且被正确包含(通过头文件或源码声明)。 ### 2. 检查指针类型与解引用操作 若`cfg`是**指向指针的指针**(如`rfc_sig_cfg_type **cfg`),需双重解引用: ```c // 错误写法:cfg->logical_device (假设cfg是二级指针) // 正确写法: (*cfg)->logical_device; ``` - **验证方法**:检查变量声明语句,例如: ```c rfc_sig_cfg_type *cfg; // 一级指针,使用 cfg->member rfc_sig_cfg_type **cfg; // 二级指针,使用 (*cfg)->member ``` ### 3. 排查头文件包含问题 若结构体定义在头文件中,需确认是否包含: ```c #include "rfc_sig_cfg_type.h" // 假设定义在此头文件 ``` - **常见问题**:头文件路径错误、条件编译未生效或头文件内容被错误修改[^1]。 ### 4. 检查变量作用域与命名冲突 若存在多个同名结构体定义(如不同作用域或文件),需明确作用域: ```c // 示例:使用命名空间限定 namespace module1 { struct rfc_sig_cfg_type { /* ... */ }; } // 使用时需指定作用域 module1::rfc_sig_cfg_type *cfg; ``` ### 5. 验证代码逻辑(结合用户提供的代码) 用户代码中出现的`cfg->logical_device`和`ptr->start->logic`可能存在问题: - **指针链式访问**:确保`ptr->start`返回的是结构体/联合体指针: ```c // 假设ptr是结构体指针,且start成员是另一结构体指针 ptr->start->logic; // 合法访问 ``` ### 解决方案总结 1. **补充结构体定义**:确保`rfc_sig_cfg_type`是明确定义的结构体。 2. **修正指针解引用**:根据指针层级使用`->`或`(*ptr)->`。 3. **检查依赖文件**:确认相关头文件已包含且无冲突。 4. **代码示例修正**: ```c // 正确示例(假设cfg是一级指针) rfc_sig_cfg_type *cfg = get_config(); int device = cfg->logical_device; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值