从成员指针到整个结构/类的强制转换
Casting from member pointer to whole struct/class
考虑以下代码:
#include <iostream>
struct bar {
double a = 1.0;
int b = 2;
float c = 3.0;
};
void callbackFunction(int* i) {
auto myStruct = reinterpret_cast<bar*>(i) - offsetof(bar, b);
std::cout << myStruct->a << std::endl;
std::cout << myStruct->b << std::endl;
std::cout << myStruct->c << std::endl;
//do stuff
}
int main() {
bar foo;
callbackFunction(&foo.b);
return 0;
}
我必须定义一个回调函数,并且我想在该函数中使用一些附加信息。我定义了自己的结构,并将成员的地址传递给函数。在函数中,我想通过强制转换"检索"整个结构,但指针似乎不匹配,我得到了错误的结果。我想我选角时做错了什么,但我不确定是什么?
您缺少一个使其工作的强制转换。在减去偏移量之前,您需要强制转换为字节类型,然后重新转换回bar*
。原因是宏offsetof
以字节数的形式返回偏移量。进行指针运算时,根据指针类型的大小进行减法和加法运算。让我们举一个例子:
假设您有一个bar
实例,名为b
,位于地址0x100h。假设sizeof(double) == 8
、sizeof(int) == 4
和sizeof(float) == 4
,那么sizeof(bar) == 16
和您的结构及其成员在内存中会是这样的:
b @ 0x100h
b.a @ 0x100h
b.b @ 0x108h
b.c @ 0x10Ch
则CCD_ 9将等于CCD_。您的原始代码说"将0x108h视为指向bar
类型的结构。"。然后给我地址为0x108h - 8 * sizeof(bar)
的bar
结构,或者具体地说:0x108h-0x80h=88h希望这个例子能说明为什么原始代码执行了错误的计算。
这就是为什么你需要告诉编译器,你想减去作为字节的地址,以获得结构中第一个成员的正确地址。
解决方案看起来像这样:
bar* owner = reinterpret_cast<bar*>(reinterpret_cast<char *>(i) - offsetof(bar, b));
有一件事你应该非常小心:只有当bar
是标准布局时,这才是合法的。您可以使用模板std::is_standard_layout<bar>::value
进行静态断言,以验证您没有意外调用UB。
问题是,对于reinterpret_cast<bar*>(i)
,您基本上将i
视为指向bar
结构数组的第一个元素的指针。
这是有问题的,因为对于任何指针(或数组(p
和索引i
,表达式*(p + i)
恰好等于p[i]
。
因此整个表达reinterpret_cast<bar*>(i) - offsetof(bar, b)
与&(reinterpret_cast<bar*>(i))[-offsetof(bar, b)]
基本相似。也就是说,您得到一个指向这个"数组"中元素-offsetof(bar, b)
的指针。这当然不是一个正确的索引。
如果你有一个字节的"数组",而不是bar
结构的"阵列",它就会起作用:
char* tempPtr = reinterpret_cast<char*>(i) - offsetof(bar, b);
bar* myStructPtr = reinterpret_cast<bar*>(tempPtr);
您将指针向后移动了太多,b
的偏移量是sizeof(double)
,所以可能是8,但表达式reinterpret_cast<bar*>(i) - offsetof(bar, b)
将其移动了sizeof(bar) * sizeof(double)
。
虽然从技术上讲,将struct/class
投射到其第一个成员是合法的,但您永远不应该需要这样做,这很容易导致UB。
如果你只切换int
和double
成员,那么int
成员是第一个,那么,因为你的类是标准布局,你可以简单地将reinterpret_cast
struct
,并正常访问其他成员,因为第一个非静态数据成员和类对象将是指针可交换:
struct bar { // Must be standard-layout!
int b = 2; // Must be first non-static data member!
double a = 1.0;
float c = 3.0;
};
void callbackFunction(int* i) {
auto myStruct = reinterpret_cast<bar*>(i);
std::cout << myStruct->a << std::endl;
std::cout << myStruct->b << std::endl;
std::cout << myStruct->c << std::endl;
//do stuff
}
int main() {
bar foo;
callbackFunction(&foo.b);
return 0;
}
- JSON转换为nlohmann JSON-lib中的结构数组
- NLOHMANN 的 JSON 库将数组转换为结构向量
- 从成员指针到整个结构/类的强制转换
- 在nlohmann json中,如何将嵌套对象的数组转换为嵌套结构的向量
- 将字符数组转换为时间结构
- 使用带有链表的堆栈数据结构将中缀转换为后缀
- i2d_X509_REQ_INFO无法正确转换req_info结构
- 在C++中将结构转换和存储为二进制
- 将字符数组转换为结构时出现问题. 结构的字符数组变量溢出
- 转换包含向量 C++ 的语法和结构
- 正在转换结构数据的字节序
- 如何大致将 cpp 代码结构转换为 python
- 将结构 std::memcpy 转换为具有足够容量的 std::vector 是未定义的行为<char>吗?
- C++ 向下转换结构
- 尝试在C++中转换结构时出现奇怪的错误消息
- 向向下转换结构添加属性值会导致分段错误
- C++基于子类型动态强制转换结构
- 从字节缓冲区强制转换结构
- c++中的类型转换结构
- c++的类型转换结构指针指向类型值