我有一个名为offset_ptr
的类,它像指针一样工作,但将其所指内存地址作为相对于其自己的地址this
的偏移量进行存储。下面是一个已删除了不必要内容以展示问题的版本:
template <typename T>
struct offset_ptr {
using offset_t = int64_t;
static constexpr auto const NULLPTR_OFFSET =
std::numeric_limits<offset_t>::max();
offset_ptr(T const* p)
: offset_{p == nullptr ? NULLPTR_OFFSET
: static_cast<offset_t>(
reinterpret_cast<uint8_t const*>(p) -
reinterpret_cast<uint8_t const*>(this))} {}
T* get() {
return
offset_ == NULLPTR_OFFSET
? nullptr
: reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset_);
}
offset_t offset_;
};
这段代码无法在使用GCC -O2
和 -O3
时正常运行:
int* get() {
offset_ptr<int> ptr = static_cast<int*>(malloc(sizeof(int)));
auto p = ptr.get();
*p = 110; // WOW - please do not optimize me away :-(
return p;
}
(为保持简单,故意省略了内存管理和错误检查!)
在生成的汇编代码中也可以看到这一点: https://godbolt.org/z/PfZEJM 只是缺少赋值操作。
如上所示的GodBolt编译器资源管理器链接,在以下情况下可以工作:
- 分配的值直接在函数本身中使用。 -
offset_ptr
位于堆上,而不是堆栈上。
- 不使用任何 offset_ptr
。对于以下内容可以正常工作:
- Clang(带有和不带优化)。 - MSVC(调试模式和发布模式)。 - GCC(最新版本和旧版本)
-O0
和 -O1
(但是没有-O2
和-O3
)。当执行时,GCC和Clang地址和UB sanitizer构建不会指示任何问题(除了泄漏的内存)。
有人能指出C++标准文档中哪个部分说此代码存在未定义行为(这可能是GCC积极优化掉赋值的原因)。或者这是GCC的一个错误?
编辑: 删除
offset_ptr
中的nullptr
检查有帮助(https://godbolt.org/z/5HjcLY)。 但我需要这些空指针检查。
offset_ptr
类型吗?或者上面的代码可以修复吗? - Felix Gündlingoffset_ptr
这样的类型(包括使用它的数据结构,例如自定义向量或unique_ptr类)可以通过仅进行转换来反序列化,当它存储指向序列化缓冲区中指向对象的偏移量时。 - Felix Gündlingthis
和p
之间没有一些进程的内存 - 它们没有被写入缓冲区呢? - Rakete1111