【问题标题】:What are the alignment limitations of the standard global default operator new?标准全局默认运算符 new 的对齐限制是什么?
【发布时间】:2012-10-17 07:54:35
【问题描述】:

我正在处理一些使用 ATL 的 CComBSTR 类型的旧代码。我正在更改它,以便它可以使用 ATL 不附带的 Visual C++ Express Edition 进行编译。我只使用了CComBSTR 的一小部分,所以这样做相当简单。

但是,在分配BSTR 内存块时,我需要用4 字节长度的前缀填充前四个字节。我担心如果我使用new char[size] 表达式为字符串分配内存,我会由于分配的char 数组没有正确对齐四字节前缀而导致对齐错误。

标准中是否有任何内容说明new 的返回值具有哪些对齐要求?我在 C++11 中看到的只有:

5.3.4/1 [expr.new]
是否支持过度对齐的类型由实现定义(3.11)。

3.11/6 [basic.align]
可以使用 alignof 表达式 (5.3.6) 查询完整类型的对齐要求。此外,char、signed char 和 unsigned char 类型应具有最弱的对齐要求。 [注意:这使得字符类型可以用作对齐内存区域(7.6.2)的基础类型。—结束注释]

我觉得这有点令人困惑——“最弱的对齐要求”对我来说是“对对齐的最严格限制”,但下面的注释似乎表明标准的意思相反。

像这样使用new char[sizeof(uint32_t) + 2*(length + 1)] 缓冲区作为BSTR 安全吗?

编辑:我刚刚意识到,在BSTR 这种特定情况下,无论如何都需要使用 SysAllocString 来分配字符串;但我仍然对以这种方式使用new 是否可以感兴趣。

【问题讨论】:

  • 在实践中您不太可能遇到对齐问题,尽管标准不会对此作出任何说明。如果您使用malloc(),您可能会发现它返回的内存块是 8 字节甚至 16 字节对齐的(因为它返回的数据必须充分对齐才能与任何基本类型一起使用); new 等人的行为似乎是合理的,但不能保证。
  • @JonathanLeffler:malloc 的标准明确表示 (C99, 7.20.3/1) The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).) -- 这对我来说似乎是一个很好的保证。
  • 是的——对于malloc(),这很好;并且“可能 8 字节或 16 字节对齐 [ment]”是因为该保证加上对 32 位和 64 位系统类型的正常限制。但是operator newnew 运算符与malloc() 不同,我不确定当您要求new char[17] 时,C++ 标准对返回值做出了什么承诺。这就是为什么这些也是 cmets,而不是答案。我懒得去看实际的 C++ 标准。

标签: c++ visual-c++ com memory-alignment


【解决方案1】:

这是一个实现细节,但 MSVC 使用操作系统分配器。 HeapAlloc() 用于 CRT 分配,CoTaskMemAlloc() 用于 COM 类型包装器,如 _bstr_t。在 32 位和 64 位代码中,它们都按 8 对齐。

您不应该使用 new 运算符为 BSTR 分配内存,必须使用 COM 分配器来确保使用正确的堆释放它们。在使用 BSTR 的任何互操作场景中都很重要,它是标准的自动化类型。 CoTaskMemAlloc/Free() 是必需的,但始终使用 BSTR 辅助函数来确保它们被正确初始化。 SysAllocString() 和 SysFreeString()。使用 SysAllocStringLen() 处理包含嵌入零的字符串。

【讨论】:

    【解决方案2】:

    5.3.4/1 [expr.new]

    是否支持过度对齐类型由实现定义(3.11)。

    这里有一件重要的事情:over-aligned 意味着比任何内置类型都更对齐。例如,在 64 位机器上,指针通常是 8 字节对齐的,因此在那些机器上过度对齐意味着对齐严格大于 8。

    因此,over-aligned 仅在使用向量类型时才值得关注,例如 SSE 或 AVX 指令或 C/C++ 的某些变体(如 Open CL)所需的那些。在日常编程中,您从内置类型中创建的类型永远不会过度对齐。

    §3.11 对齐 [basic.align]

    3/扩展对齐由大于alignof(std::max_align_t) 的对齐表示。是否支持任何扩展对齐以及支持它们的上下文是实现定义的(7.6.2)。具有扩展对齐要求的类型是 过度对齐 类型。

    9/ 如果实现不支持在特定上下文中进行特定扩展对齐的请求,则程序格式错误。 此外,对于动态存储的运行时分配请求,如果所请求的对齐方式无法得到满足,则应将其视为分配失败。

    此外,习惯上new 返回与alignof(std::max_align_t) 对齐的内存。这是因为常规的::operator new 只知道要分配的对象的大小,而不知道它的对齐方式,因此需要满足程序中可能的最强对齐要求。

    另一方面,注意堆栈上分配的char 数组,不能保证它的对齐方式最终会是什么。

    【讨论】:

    • 是的;在我一直使用的堆栈上 std::aligned_storage .+1
    • “无法接受所请求的对齐方式” -- 什么是“所请求的对齐方式”?
    • @BillyONEal:我必须承认我不太确定(在这种情况下)。在 §17.6.3.5/6 中,据说为具有 requested alignment 他们不支持的类型实例化的分配器可能无法实例化(编译时失败),分配时抛出(运行时失败)或忽略它。这很清楚,因为分配器是在类型上模板化的,因此知道它的对齐方式。但是对于::operator new(size_t),我不太明白除了alignof(std::max_align_t)之外它还能是什么。
    【解决方案3】:

    您不应该尝试对 BSTR 使用 C++ 内存管理函数 - 它们只能使用 SysAllocString() 系列函数分配。这保证了获得BSTR的任何人都可以在获得的BSTR上使用SysFreeString()和家族的其他功能。如果您违反此要求,您的程序将遇到未定义的行为。

    【讨论】:

    • 1.我相信汉斯的回答在一天前已经很好地涵盖了这一点。 2. 我相信我已经更新了我的问题。 3. 并非总是如此。如果将 BSTR 传递给在概念上不拥有该 BSTR 所有权的函数,则使用什么分配函数无关紧要。
    • @Billy ONeal:实际上你对第 3 点的看法是错误的。即使一个函数没有获得字符串的所有权,它仍然可以使用 SysStringLen(),这依赖于正确的字符串分配。
    • SysStringLen() 依赖于存在的长度前缀。它不关心字符串是从哪个堆分配的。
    • @Billy ONeal:严格来说,它依赖于SysAllocString() 等制作的一些特定于实现的内部数据。尝试在用户代码中编写它并不是最佳实践的示例。
    • 我想我们可以同意在这一点上不同意。 SysAllocString 完全允许不分配 BSTR 的格式——如果这是真的,那么编译器在 BSTR() 宏中所做的操作将是非法的,而事实并非如此。这不是实现细节,而是 COM 的一个有据可查的合同部分。
    猜你喜欢
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    • 2015-06-21
    • 2023-03-04
    • 2013-07-07
    • 2018-07-12
    • 1970-01-01
    相关资源
    最近更新 更多