【问题标题】:Safe way to reinterpret_cast raw struct at particular offset and type?以特定偏移量和类型重新解释原始结构的安全方法?
【发布时间】:2011-09-25 06:10:19
【问题描述】:
return *reinterpret_cast<UInt32*>((reinterpret_cast<char*>(this) + 2));

Struct 是 pragma packed 1 并且包含一堆 uint、char、short 字段...

既然是 UInt32,是不是应该先 reinterpret_cast 转为 unsigned char* 还是有关系?

另外,速度在这里很关键,我相信 reinterpret_cast 是最快的演员而不是 static_cast。

编辑:该结构实际​​上由两个单字节字段组成,后跟大约 16 个其他结构的联合,其中 15 个将 UInt32 作为其第一个字段。我快速检查它不是没有的,然后对 2 字节偏移量执行 reinterpret_cast。

【问题讨论】:

  • 我相信reinterpret_caststatic_cast
  • @Nawaz 该声明有任何引用吗?
  • @Nawaz:真的吗?似乎不太可能..
  • 你从哪里得到2?如果它是一个变量,包含结构的 Uint32 成员的已知偏移量,但根据某些运行时属性,它在运行时将是不同的成员,那么也许您可以使用指向成员的指针。如果您正在阅读一些 ASN.1 风格的不同数据的混合,它会告诉您自身各个位的偏移量,那么获取 4 字节整数的可移植方式是一次一个字节,移位和 ORing。如果你想要“快速”,你需要做一些特定于实现的事情。 UInt32 看起来像 Windows,但谁知道呢。
  • @MSalters:对优化器没有影响。演员表基本上告诉编译器信任用户。在那之后,类型由用户指定,编译器/优化器不在乎你是否犯了错误。

标签: c++ casting struct reinterpret-cast


【解决方案1】:

您不能直接访问该成员吗?这是未定义的行为,在强制单词对齐的系统上根本不起作用(这可能不是问题,因为您正在这样做,但需要提及)。

reinterpret_cast 不会比static_cast 快,因为它们只是告诉编译器在编译时如何使用内存。但是dynamic_cast 会慢一些。

没有合法的方式将您的struct + offset 视为非char 类型。

【讨论】:

  • 请看我下面的评论。我只是直接访问它,但是该项目的位置在一个联合中,并且在执行 reinterpret_cast 之前我正在进行预验证
【解决方案2】:

reinterpret_caststatic_cast 应该具有相同的运行时间——除非需要执行数字转换,否则接近于零。您应该选择使用的演员不是基于“速度”,而是基于正确性。如果您在谈论dynamic_cast,您可能有理由争论,但reinterpret_caststatic_cast 通常会导致(最坏的情况)寄存器副本(例如,从整数寄存器到浮点寄存器)。 (假设没有用户定义的转换运算符进入图片,那么它是一个带有所有相关内容的函数调用)

没有安全的方法来做你正在做的事情。这违反了strict aliasing 规则。如果您想做这样的事情,您的struct 需要采用某种形式的union,您可以通过联合访问UInt32

最后,如前所述,该示例在任何存在对齐问题的平台上都会失败。这意味着你在 x86 上会很好,但在 x64 上就不行了。

【讨论】:

  • 我喜欢你的回答。请检查我编辑的帖子,因为您可能会感兴趣。
  • @chriskirk:如果是这样,没有理由reinterpret_cast - 只需访问这 16 个联合中的任何一个中的成员。它们会占用相同的内存空间,对吧?
【解决方案3】:

您忘了提及,您使用的是指向结构的指针,而不是结构本身,无论如何,我发现对结构的特定字段使用指针算术是不必要的。编译器和生成的代码在使用指针算法时不会更快,并且会使您的代码更加复杂,但不必要:

struct AnyInfoStruct {
   char Name[65]; 
   char Address[65]; 
   short Whatever; 
   uint Years;
   union AExtraData { 
      int A; 
      char B; 
      double C; 
   } ExtraData
}; 

// recieves generic pointer, hidding struct fields:
void showMsg(void* AnyPtr)
{
  AnyInfoStruct* MyAnyInfo = &(static_cast<*AnyPtr>);
  cout << "Years: " << MyAnyInfo->Years << "\n";

  cout << "ExtraData.A: " << MyAnyInfo->ExtraData.A << "\n";
}

void main()
{
  AnyInfoStruct* MyAnyInfo; 

  // hide struct into a ptr
  void* AnyPtr = AnyInfoStruct;

  showMsg(MyAnyInfo);
}

干杯。

UPDATE1:在示例中添加了“联合”。

【讨论】:

  • 实际上我试图访问的成员在结构内的联合中
【解决方案4】:

既然你说结构包含整数和短裤,我会冒昧地回答,假设这个联合是 POD。如果是这样,那么您将从 9.5/1 中受益:

一个特殊的保证是在 为了简化联合的使用: 如果一个 POD 联合包含多个 共享一个共同点的 POD 结构 初始序列(9.2),如果一个 此 POD 联合类型的对象包含 POD 结构之一,它是 允许检查共同点 任何 POD 结构的初始序列 成员

因此,假设您的结构如下所示:

struct Foo1 { UInt32 a; other stuff; };
struct Foo2 { UInt32 b; other stuff; };
...
struct Foo15 { UInt32 o; other stuff; };
struct Bar { UInt16 p; other stuff; };

// some kind of packing pragma
struct Baz {
    char is_it_Foo;
    char something_else;
    union {
        Foo1 f1;
        Foo2 f2;
        ...
        Foo15 f15;
        Bar b;
    } u; 
};

那么你可以这样做:

Baz *baz = whatever;
if (baz->is_it_Foo) {
    UInt32 n = baz->u.f1.a;
}

如果联合的成员不是 POD,那么您的 reinterpret_cast 无论如何都会被破坏,因为不再保证结构的第一个数据成员位于从结构开始的偏移量 0 处。

【讨论】:

    猜你喜欢
    • 2016-10-15
    • 1970-01-01
    • 2018-02-17
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-25
    • 1970-01-01
    相关资源
    最近更新 更多