C++ 从指向某个成员的指针获取指向对象的指针
假设有一个结构C++ 从指向某个成员的指针获取指向对象的指针,c++,pointers,c++11,language-lawyer,C++,Pointers,C++11,Language Lawyer,假设有一个结构 struct Thing { int a; bool b; }; 我得到一个指向该结构的成员b的指针,比如说作为某个函数的参数: void some_function (bool * ptr) { Thing * thing = /* ?? */; } 如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,我想要标准定义的行为,而不是未定义或实现定义的行为 作为旁注:我知道这规避了类型安全 void some_function (bool *
struct Thing {
int a;
bool b;
};
我得到一个指向该结构的成员b
的指针,比如说作为某个函数的参数:
void some_function (bool * ptr) {
Thing * thing = /* ?? */;
}
如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,我想要标准定义的行为,而不是未定义或实现定义的行为
作为旁注:我知道这规避了类型安全
void some_function (bool * ptr) {
Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}
我认为没有UB。如果您确定指针确实指向结构中的成员
b
,就像有人这样做一样
Thing t;
some_function(&t.b);
然后,您应该能够使用宏获取指向结构的指针:
std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<char*>(ptr) - offset);
std::size\u t offset=offset of(Thing,b);
事物*事物=重新解释投射(重新解释投射(ptr)-偏移);
请注意,如果指针ptr
实际上没有指向Thing::b
成员,那么如果使用指针Thing
X*get\u ptr(bool*b),上述代码将导致未定义的行为{
X* get_ptr(bool* b){
static typename std::aligned_storage<sizeof(X),alignof(X)>::type buffer;
X* p=static_cast<X*>(static_cast<void*>(&buffer));
ptrdiff_t const offset=static_cast<char*>(static_cast<void*>(&p->b))-static_cast<char*>(static_cast<void*>(&buffer));
return static_cast<X*>(static_cast<void*>(static_cast<char*>(static_cast<void*>(b))-offset));
}
静态typename std::aligned_storage::type buffer;
X*p=静态_转换(静态_转换(&buffer));
ptrdiff_t const offset=static_cast(static_cast(&p->b))-static_cast(static_cast(&buffer));
返回static_cast(static_cast(static_cast(b))-offset);
}
首先,我们创建一些可以保存X
的静态存储。然后我们得到缓冲区中可能存在的X
对象的地址,以及该对象的b
元素的地址
回到
char*
,我们可以得到缓冲区内bool
的偏移量,然后我们可以使用它将指向实bool
的指针调整回指向包含X
的指针。我的建议源自@Rod answer in,类似于@0xbadf00d的one in
我开始想象一种偏移量的形式,它驱动指向类数据成员的指针的实现,后来这篇文章和我所做的测试证实了这一点
<>我不是C++实践者,很抱歉,简洁。
#include <iostream>
#include <cstddef>
using namespace std;
struct Thing {
int a;
bool b;
};
template<class T, typename U>
std::ptrdiff_t member_offset(U T::* mem)
{
return
( &reinterpret_cast<const char&>(
reinterpret_cast<const T*>( 1 )->*mem )
- reinterpret_cast<const char*>( 1 ) );
}
template<class T, typename U>
T* get_T_from_data_member_pointer (U * ptr, U T::*pU) {
return reinterpret_cast<T*> (
reinterpret_cast<char*>(ptr)
- member_offset(pU));
}
int main()
{
Thing thing;
thing.b = false;
bool * ptr = &thing.b;
bool Thing::*pb = &Thing::b;
std::cout << "Thing object address accessed from Thing test object lvalue; value is: "
<< &thing << "!\n";
std::cout << "Thing object address derived from pointer to class member; value is: "
<< get_T_from_data_member_pointer(ptr, &Thing::b) << "!\n";
}
#包括
#包括
使用名称空间std;
结构物{
INTA;
布尔b;
};
模板
std::ptrdiff\U t成员\U偏移量(U t::*mem)
{
返回
(&reinterpret_cast(
重新解释(1)->*mem)
-重新解释(一),;
}
模板
T*从数据成员指针获取数据(U*ptr,U T::*pU){
返回重新解释(
重新解释铸件(ptr)
-成员(pU);;
}
int main()
{
事物;
事物b=虚假;
bool*ptr=&thing.b;
布尔事物::*pb=&事物::b;
std::从bool
地址中扣除Thing
指针不是很难吗?为什么不把Thing地址/指针传递给函数?@Stefan它应该是分配器的一部分:它有带簿记和数据的节点,我在分配时给数据一个指针,然后在释放时得到它。我刚刚注意到你了r在分配程序中对此有何评论。这意味着您不能保证它是标准布局,而且offsetof
可以是未定义的。您可能从2015年11月起就开始了这一过程,但如果它仍然相关,并且出现了一个非offsetof
的答案,我建议您实现它。幸运的是,对于分配程序,您可以不需要返回指针。您可以将满足NullablePointer
和RandomAccessIterator
和typedef
的任何类似指针的结构返回到pointer
。只是想知道:如果您需要从某个成员处获取对象,您不能简单地将对象作为指针传递的原因是什么成员的ead?AFAIKchar*
对于这种指针算法来说是完全安全和规范的。好吧,这里太混乱了:)char不能小于8位,因为最小范围,如果它大于8位,int8\u t
甚至不可能存在,因为地址和大小都很好。“这就是使用int8_t
的实际意义,它被定义为char
可能不是的字节”-不,你完全弄错了。在这里使用char
,int8_t
是错误的。还要注意,offsetof
对于非标准布局类型有未定义的行为(C++11)在实践中,它通常仍能按预期工作。术语“标准布局”大约是指“不使用C所没有的任何功能的东西”原来的代码< >偏移实际上是C特征,而不是C++,而C++规范明确地表示它在不标准布局的类上是不定义的。参见CPUPACTY。您是指声明“代码>缓冲区< /代码>为<代码>静态类型名STD::AligNeDySturt::Type < /代码>?是否有理由<代码> STA?tic\u cast
通过void*
而不是简单地重新解释\u cast
直接到char*
和X*
?是的,很好。我不喜欢重新解释\u cast
,因为映射没有明确规定。静态\u cast
有一个定义的映射。我想这在一般情况下是有意义的,尽管重新解释指针的et_cast
定义得很好。我不喜欢这样保留额外的空间,但在其他方面它似乎很好。我非常担心这个X
和参数1之间的偏移量不一样,但是如果编译器这样做的话(对于非标准布局,我找不到任何证据,但我怀疑是否有证据),我不确定有什么办法可以绕过它。很抱歉,我昨天没有及时赶到这里分配全部奖金;这似乎仍然是最好的解决方案。虽然我确实喜欢@rfb为它制作了一个通用函数,但由于对齐问题,我不确定它是否符合标准。很抱歉,如果我的答案有价值,我认为它是not用于泛型,而是因为它提出了