【问题标题】:std::pair<U,V> alignment controlstd::pair<U,V> 对齐控制
【发布时间】:2013-08-16 03:59:26
【问题描述】:

我注意到在尝试将 std::pair 保存到二进制文件时发生的一件不愉快的事情:std::pair 与一个单词对齐。它在处理器效率方面可能很有用,但需要更多存储空间,因此我想将对齐模式切换为 std::pair 的 1 字节。我的编译器是 MS VC++ 2012。

#include <iostream>

int main( )
{
    struct S_a { double a; size_t b; };

#pragma pack(1)
    struct S_wa { double a; size_t b; };

    std::cout << sizeof( size_t ) << '\n';                          // 4
    std::cout << sizeof( double ) << '\n';                          // 8
    std::cout << sizeof( std::pair< size_t, size_t > ) << '\n';     // 8
    std::cout << sizeof( std::pair< double, size_t > ) << '\n';     // 16 - bad
    std::cout << sizeof( S_wa ) << '\n';                            // 12 - good
    std::cout << sizeof( S_a ) << '\n';                             // 16
    std::cout << sizeof( std::pair< double, double > ) << '\n';     // 16
}

我试过了,但它不起作用:

#pragma pack(1)
    typedef std::pair< double, size_t > Q;

    std::cout << sizeof( Q ) << '\n';                               // 16

【问题讨论】:

  • 在项目属性下,尝试更改C/C++ -> Code Generation -> Struct Member Alignment 选项 (/Zp1)
  • 与其搞砸你的运行时,不如写一些特殊的函数来紧凑地处理读写对对象不是更好吗?

标签: c++ memory-alignment std-pair


【解决方案1】:

A std::pair 基本上归结为:

class xxx
{
   T1 _t1;
   T2 _t2;
};

两个成员的对齐方式由在定义模板本身时生效的对齐方式定义,而不仅仅是在用于实例化实例时。

STL 的 Microsoft 实现使用符号 _CRT_PACKING 来定义用于所有 STL 组件的打包。默认情况下,包装设置为 8。如果您在包含定义 std::pair (&lt;utility&gt;) 的标头之前自己定义此符号,理论上您可以覆盖包装并设置自己的包装。

请注意调用库或采用标准打包的其他代码时可能遇到的潜在问题。

【讨论】:

    【解决方案2】:

    抱歉,pack 编译指示在这种情况下对您不起作用。你可以

    #define _CRT_PACKING 1
    #include <utility>
    

    这可能会导致各种问题。其中之一是某些 API,尤其是低级 API,期望数据以某种方式对齐。并非所有人都足够聪明,可以处理不适合的情况;这可能导致不礼貌的崩溃。

    如果你真的希望对象像这样被序列化,请自己处理std::pair&lt;U,V&gt;的序列化(仅示例):

    template<typename U, typename V>
    void paircpy(char *dest, const std::pair<U, V> &pair) {
        memcpy(buffer, &pair.first, sizeof(U));
        memcpy(buffer + sizeof(U), &pair.second, sizeof(V));
    }
    

    您可能希望处理不适合memcpy 的数据类型的特殊情况。

    对于任何严肃的项目,您真正应该做的是以可移植的方式序列化对象,以便它们可检索并优雅地处理浮点、签名数据类型的不同编码、指针 、数组、STL 容器以及其他无法简单转储对象内存或不足的任何其他内容。

    阅读C++ FAQ 并开发您自己的序列化模块,这些模块不仅仅是将对象的内存表示转储到文件中。

    或者,您可以使用预打包的便携式解决方案来序列化数据类型,例如

    【讨论】:

    • 好的。我有一个类 ArrayFile 处理 std::vector,附加到磁盘上的文件并且已经做了一些编组。我认为,将 ArrayFile 明确专门化为 T=std::pair 并减少那里的对齐,而不更改内存分配是正确的。好吗?
    • #pragma pack before #include &lt;utility&gt; 不会做任何事情,因为&lt;utility&gt; 使用_CRT_PACKING 宏定义了自己的包装。
    • @JonathanPotter 已修复。
    • @AlexanderMihailov 实际上,以可移植的方式编组对象会更好。
    【解决方案3】:

    好的,完全不同的“解决方案”可能是一个好主意,也可能不是一个好主意,但是嘿,如果它有效,我是谁来阻止你知道。它带有与其他答案相同的所有注意事项,但允许您挑选毒药。

    对您关心的类型进行std::pair 的部分特化,使用该特定定义的编译指示包,例如。对于charint

    namespace std {
    #pragma pack( /* ... whatever you want ... */ )
    template<> struct pair<char,int> {
      char first;
      int second;
    };
    }
    

    在您可以触摸到定义的地方,您可以影响包装。同样,只有当您愿意为所有相关类型都这样做时,这才是好的,并记住其他海报提到的警告。

    你正在使用的库很可能很疯狂,并决定在 pair 中放置更多库的其余部分所依赖的东西,但你可能不需要担心它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      • 1970-01-01
      相关资源
      最近更新 更多