一种使用指向结构中声明的字段的指针来计算指向整个结构的指针的可移植方法(又名CONTAINING_RECORD宏) [英] A portable way to calculate pointer to the whole structure using pointer to a field declared inside the structure (aka CONTAINING_RECORD macro)

查看:90
本文介绍了一种使用指向结构中声明的字段的指针来计算指向整个结构的指针的可移植方法(又名CONTAINING_RECORD宏)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,在Winnt.h中定义了众所周知的CONTAINING_RECORD()宏:

There is the well known CONTAINING_RECORD() macro defined, for instance, in Winnt.h:

#define CONTAINING_RECORD(address, type, field) ((type *)( \
                                              (PCHAR)(address) - \
                                              (ULONG_PTR)(&((type *)0)->field)))

或在FreeBSD中:

or in FreeBSD:

#define CONTAINING_RECORD(addr, type, field)    \
      ((type *)((vm_offset_t)(addr) - (vm_offset_t)(&((type *)0)->field)))

或在Linux中:

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

,当然,在世界上许多其他地方。

and, surely, in many other places worldwide.

但是,我怀疑他们是否符合标准。

However, I doubt they are standard compliant.

Boost源(boost_1_48_0 / boost / intrusive / detail / parent_from_meber.hpp)而不是
disapoint我 - 他们有3 #ifdef PARTICULAR_COMPILER个案:

Boost sources (boost_1_48_0/boost/intrusive/detail/parent_from_meber.hpp) rather disapoint me - they have 3 #ifdef PARTICULAR_COMPILER cases:

template<class Parent, class Member>
inline std::ptrdiff_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member)
{
   //The implementation of a pointer to member is compiler dependent.
   #if defined(BOOST_INTRUSIVE_MSVC_COMPLIANT_PTR_TO_MEMBER)
   //msvc compliant compilers use their the first 32 bits as offset (even in 64 bit mode)
   return *(const boost::int32_t*)(void*)&ptr_to_member;
   //This works with gcc, msvc, ac++, ibmcpp
   #elif defined(__GNUC__)   || defined(__HP_aCC) || defined(BOOST_INTEL) || \
         defined(__IBMCPP__) || defined(__DECCXX)
   const Parent * const parent = 0;
   const char *const member = reinterpret_cast<const char*>(&(parent->*ptr_to_member));
   return std::ptrdiff_t(member - reinterpret_cast<const char*>(parent));
   #else
   //This is the traditional C-front approach: __MWERKS__, __DMC__, __SUNPRO_CC
   return (*(const std::ptrdiff_t*)(void*)&ptr_to_member) - 1;
   #endif
}

第二种情况)

所以我的问题是:


  1. 至少还有CONTAINING_RECORD和container_of宏实现之一符合标准吗?

  1. Is at least one of CONTAINING_RECORD aka container_of macro implementations standard compliant?

如果没有,是否存在一个标准兼容的方法使用指向结构中声明的字段的指针来计算指向整个结构的指针?

If not, does there exist a standard compliant way to calculate pointer to the whole structure using pointer to a field declared inside the structure?

如果C和C ++的答案不同,

If answers differ for C and C++, I'm interested in both cases.

推荐答案


  1. 不,没有人给你的兼容。引用空指针不是 typeof 不是和({...})表达式不是

是的,linux的第二部分正确重写了(type *)((char *)(ptr) - offsetof (type,member))符合。 ( offsetof 在标准中定义)

yes, the second part of the linux thing rewritten properly (type *)((char *)(ptr) - offsetof(type, member)) is compliant. (offsetof is defined in the standard)

请参阅2

AFAIK,所有这些对C和C ++都有效

AFAIK, all this is valid for C and C++

em> linux的东西试图通过检查指向成员的指针和参数中的指针是否兼容赋予宏的附加安全性。就我可以看到gcc扩展 typeof 是C中这种方法的必要条件。

The linux thing tries to add additional security to the macro by checking if a pointer to the member and the pointer in the argument are assignment compatible. As far as I can see the gcc extension typeof is essential for such an approach in C.

对于C99,你可以使用一个复合文字来做一些比较弱的检查。

With C99, you could though use a "compound literal" to have a somewhat weaker check by doing something like

(type){ .member = *(ptr) }

但这只会告诉你 * ptr 是与成员兼容的赋值。例如如果 ptr 会是 float * 和成员 double 仍然可以工作。

but this would only tell you if the type of *ptr is assignment compatible to member. E.g if ptr would be float* and member double this would still work.

在任何情况下,类型的检查只会给你假的安全性。你必须确定你的 ptr 真的来自类型里面才能使用。小心。

In any case, the check of the types only gives you false security. You must be pretty sure that your ptr really comes from inside type to use this. Be careful.

正如bert-jan在下面的评论中所说,在C ++中,当有虚拟继承时, offsetof 宏的根本问题是无法在编译时确定成员的偏移量。我不相信容器宏的当前想法在这样的上下文中是有意义的。 非常小心。

As bert-jan remarks in his comments below, in C++, when there is virtual inheritance, the offsetof macro has the fundamental problem that the offset of member cannot be determined at compile time. I am not convinced that the present idea of a container macro then makes any sense in such a context. Be extremely careful.

这篇关于一种使用指向结构中声明的字段的指针来计算指向整个结构的指针的可移植方法(又名CONTAINING_RECORD宏)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆