从指向某个成员的指针获取指向对象的指针

Dan*_*our 18 c++ pointers language-lawyer c++11

假设有一个结构

struct Thing {
  int a;
  bool b;
};
Run Code Online (Sandbox Code Playgroud)

我得到一个指向b该结构成员的指针,比如某些函数的参数:

void some_function (bool * ptr) {
  Thing * thing = /* ?? */;
}
Run Code Online (Sandbox Code Playgroud)

如何获取指向包含对象的指针?最重要的是:如果不违反标准中的某些规则,那就是我需要标准定义的行为,而不是未定义的行为,也不是实现定义的行为.

作为旁注:我知道这可以避免类型安全.

Som*_*ude 19

如果你确定指针真的指向b结构中的成员,就像有人那样

Thing t;
some_function(&t.b);
Run Code Online (Sandbox Code Playgroud)

然后你应该能够使用offsetof宏来获得指向结构的指针:

std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<int8_t*>(ptr) - offset);
Run Code Online (Sandbox Code Playgroud)

请注意,如果指针ptr实际上没有指向Thing::b成员,那么如果使用指针,上面的代码将导致未定义的行为thing.

  • "这是使用`int8_t`的实际点,它被定义为`char`可能不是"的字节 - 不,你完全错误的方式.在这里使用`char`,`int8_t`是错误的. (9认同)
  • 术语"标准布局"大约是指"不使用C没有的任何特征的东西".事实证明`offsetof'实际上是一个C特性,而不是C++特性,并且C++规范明确表示它不是在非标准布局的类上定义的.另见[`std :: is_standard_layout`](http://en.cppreference.com/w/cpp/types/is_standard_layout),[`offsetof`](http://en.cppreference.com/w/cpp/ types/offsetof)和[cppreference上的[非静态数据成员页面的标准布局部分](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout). (6认同)
  • AFAIK`char*`是这种指针算法的完全安全和规范的方法. (3认同)
  • 另请注意,`offsetof`对于非标准布局类型(C++ 11)具有未定义的行为.在实践中,它通常仍然按预期工作. (3认同)
  • 好吧,那里太糊涂了 :) char 不能小于 8 位,因为最小范围,如果大于 8 位,`int8_t` 甚至不能存在,因为地址和 sizeof 的东西......一切都很好。 (2认同)
  • @JoachimPileborg"这是使用int8_t的实际点,它被定义为char可能不是的字节" - 'offsetof`定义来自C,它清楚地表明字符是一个字节,所以"offset in bytes"= ="字符中的偏移量".如果`sizeof(char)`保证为1,则`char*`肯定是处理`offsetof`中的值时使用的正确类型? (2认同)

dev*_*fan 5

void some_function (bool * ptr) {
  Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}
Run Code Online (Sandbox Code Playgroud)

认为没有UB。


Ant*_*ams 5

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));
}
Run Code Online (Sandbox Code Playgroud)

首先,我们创建一些可以容纳的静态存储X.然后我们得到X缓冲区中可能存在的对象的地址,b以及该对象的元素的地址.

回到char*,我们可以得到bool缓冲区内的偏移量,然后我们可以使用它来调整指向真实bool的指针返回指向包含的内容X.