我正在阅读linux内核5.17.5,现在来看一下container_of()宏.

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({              \
    void *__mptr = (void *)(ptr);                   \
    static_assert(__same_type(*(ptr), ((type *)0)->member) ||   \
              __same_type(*(ptr), void),            \
              "pointer type mismatch in container_of()");   \
    ((type *)(__mptr - offsetof(type, member))); })

我的问题很简单:__mptr的目的是什么,我能用(void *)ptr代替__mptr吗?

#define container_of(ptr, type, member) ({          \
    static_assert(__same_type(*(ptr), ((type *)0)->member) ||   \
              __same_type(*(ptr), void),            \
              "pointer type mismatch in container_of()");   \
    ((type *)((void*)ptr - offsetof(type, member))); })

推荐答案

由于“kernel.h: handle pointers to arrays better in container_of()”__mptr不再用于类型判断.它用于消除scripts/gcc-plugins/randomize_layout_plugin.c的信息性消息,如inform(gimple_location(stmt), "found mismatched ssa struct pointer types: %qT and %qT\n", ptr_lhs_type, ptr_rhs_type);.此文件的工作之一是:

/*
 * iterate over all statements to find "bad" casts:
 * those where the address of the start of a structure is cast
 * to a pointer of a structure of a different type, or a
 * structure pointer type is cast to a different structure pointer type
 */

如果缺少__mptr,宏将包括您所说的代码:

(type *)((void*)ptr - offsetof(type, member)))

(PS:char *在这里更好,因为iso c标准保证它是byte foroffsetof, void*`仅由gnu c保证)

如果offsetof为零,ptr将包含 struct 类型member的起始地址,那么它将被转换为一个完全不同的类型:struct type *.此表单根据scripts/gcc-plugins/randomize_layout_plugin.c发生故障,并将由其检测.

随着void *__mptr = (void *)(ptr);的引入,编译器不知道__mptr和更多的类型,因此在将void * __mptr转换为(type *)时,scripts/gcc-plugins/randomize_layout_plugin.c将不再抱怨


下面是4.13之前的内核的原始答案,当时__mptr用于类型判断:

让我们简化container_of,看看这个例子:

#include <stdio.h>
#include <stddef.h>
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
#define container_of_without_typechecking(ptr, type, member) ({         \
    (type *)( (char *)ptr - offsetof(type,member) );})
struct Foo
{
    int a;
};
int main(int argc, char *argv[]) {
    struct Foo foo;
    int *a;

    printf("foo addr: %p\n", &foo);
    a = &foo.a;
    printf("container of a: %p\n", container_of_without_typechecking((unsigned long long*)a, struct Foo, a));
    printf("typecheck: container of a: %p\n", container_of((unsigned long long*)a, struct Foo, a));
    return 0;
}

container_of_without_typechecking没有__mptr,但container_of有.

编译时:

a.c: In function ‘main’:
a.c:13:55: warning: initialization of ‘const int *’ from incompatible pointer type ‘long long unsigned int *’ [-Wincompatible-pointer-types]
   13 |         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
      |                                                       ^
a.c:28:47: note: in expansion of macro ‘container_of’
   28 |     printf("typecheck: container of a: %p\n", container_of((unsigned long long*)a, struct Foo, a));
      |                                               ^~~~~~~~~~~~

如您所见,container_of会抛出incompatible pointer type警告,而container_of_without_typechecking不会,所以它只是用于类型判断.

另外,请注意,Linux内核将此警告视为错误:

KBUILD_CFLAGS   += $(call cc-option,-Werror=incompatible-pointer-types)

因此,如果将错误的类型传递到container_of,则会得到error而不是警告.

C++相关问答推荐

位屏蔽对于无符号转换是强制的吗?

与unions 的未定义行为

是否定义了数组指针类型转换为指针类型?""""

为什么我得到更多的256假阳性在PKZIP解密密钥验证?

变量>;-1如何在C中准确求值?

如何在不使用其他数组或字符串的情况下交换字符串中的两个单词?

VS代码';S C/C++扩展称C23真关键字和假关键字未定义

理解C版宏(看起来像未声明的变量?)

X64:并发写入布尔数组

CC2538裸机项目编译但不起作用

如何使用_newindex数组我总是得到错误的参数

将返回的char*设置为S在函数中定义的字符串文字可能会产生什么问题?

为什么Linux无法映射这个PT_LOAD ELF段?

将 struct 数组写入二进制文件时发生Valgrind错误

RISC-V GCC编译器错误编译ASM代码

在我的函数中实现va_arg的问题

如何不断地用C读取文件?

c如何传递对 struct 数组的引用,而不是设置 struct 的副本

使用fread()函数读取txt文件

为什么简单的 ELF 二进制文件中存在重叠和未对齐的段?