【问题标题】:Does the alignas specifier work with 'new'?alignas 说明符是否与“新”一起使用?
【发布时间】:2013-03-08 20:39:04
【问题描述】:

我的问题很简单;

alignas 说明符是否与“new”一起使用?即如果一个struct被定义为对齐,分配new时是否会对齐?

【问题讨论】:

    标签: c++ c++11 new-operator memory-alignment


    【解决方案1】:

    在 C++17 之前,如果您的类型的对齐方式没有过度对齐,那么可以,默认的 new 将起作用。 “过度对齐”表示您在alignas 中指定的对齐方式大于alignof(std::max_align_t)。默认的new 将或多或少地与非过度对齐的类型一起使用;默认内存分配器将始终分配对齐等于alignof(std::max_align_t) 的内存。

    但是,如果您的类型的对齐方式过度对齐,那么您就不走运了。无论是默认的new,还是您编写的任何全局new 运算符,都无法知道该类型所需的对齐方式,更不用说分配适合它的内存了。解决这种情况的唯一方法是重载类的operator new,这将能够查询类与alignof 的对齐情况。

    当然,如果该类被用作另一个类的成员,这将没有用。除非其他类也重载operator new。所以像new pair<over_aligned, int>() 这样简单的东西是行不通的。

    C++17 adds a number of memory allocators 给定了正在使用的类型的对齐方式。这些分配器专门用于过度对齐的类型(或者更具体地说,new-extended over-aligned types)。所以new pair<over_aligned, int>() 将在 C++17 中工作。

    当然,这只适用于分配器处理过度对齐的类型。

    【讨论】:

    • 补充:可以通过alignof(std::max_align_t)查询这个最大对齐。对齐大于此的类型称为过度对齐,它们的支持是有条件的、实现定义的。
    • 原来我其实不需要这个,不过很高兴知道!
    • @NicolBolas:x86 上的 PageTables 需要在页面边界 (0x1000) 上对齐。
    • @NicolBolas 将 alignas 与 new 结合使用的正确语法是什么?我尝试了以下 char* a = alignas(alignment) new char[N] 但这表示“'alignas'之前的预期主表达式”我无法在线找到语法示例。
    • @sunny 没有。 void* operator new(std::size_t count) 无法直接获取用户指定的对齐方式。除非您将对齐编码到计数中,否则这是非常错误的。或者您可以编写一个自定义的placement-new 运算符,它接受一个额外的size_t 进行对齐。
    【解决方案2】:

    不,它没有。该结构将被填充到请求的对齐位置,但不会对齐。但是,有可能是 allowed in C++17(这个 C++17 提案存在的事实应该很好地证明这在 C++11 中不起作用)。

    我看到这似乎适用于一些内存分配器,但这纯粹是运气。例如,一些内存分配器会将其内存分配与请求大小的 2 次方(最大 4KB)对齐,作为分配器的优化(减少内存碎片,可能更容易重用以前释放的内存等...) .但是,我测试的 OS X 10.7 和 CentOS 6 系统中包含的 new/malloc 实现并没有这样做,并且失败并显示以下代码:

    #include <stdlib.h>
    #include <assert.h>
    
    struct alignas(8)   test_struct_8   { char data; };
    struct alignas(16)  test_struct_16  { char data; };
    struct alignas(32)  test_struct_32  { char data; };
    struct alignas(64)  test_struct_64  { char data; };
    struct alignas(128) test_struct_128 { char data; };
    struct alignas(256) test_struct_256 { char data; };
    struct alignas(512) test_struct_512 { char data; };
    
    int main() {
       test_struct_8   *heap_8   = new test_struct_8;
       test_struct_16  *heap_16  = new test_struct_16;
       test_struct_32  *heap_32  = new test_struct_32;
       test_struct_64  *heap_64  = new test_struct_64;
       test_struct_128 *heap_128 = new test_struct_128;
       test_struct_256 *heap_256 = new test_struct_256;
       test_struct_512 *heap_512 = new test_struct_512;
    
    #define IS_ALIGNED(addr,size)   ((((size_t)(addr)) % (size)) == 0)
    
       assert(IS_ALIGNED(heap_8, 8));
       assert(IS_ALIGNED(heap_16, 16));
       assert(IS_ALIGNED(heap_32, 32));
       assert(IS_ALIGNED(heap_64, 64));
       assert(IS_ALIGNED(heap_128, 128));
       assert(IS_ALIGNED(heap_256, 256));
       assert(IS_ALIGNED(heap_512, 512));
    
       delete heap_8;
       delete heap_16;
       delete heap_32;
       delete heap_64;
       delete heap_128;
       delete heap_256;
       delete heap_512;
    
    return 0;
    }
    

    【讨论】:

    • 这是正确的,最佳答案不是。 C++11 将为您提供堆栈分配对象的正确对齐方式,但默认内存分配器不会为您提供正确的对齐方式。所以现在,你必须使用封装在 OS 检测宏中的 POSIX/windows 函数。例如; Linux/OSX/BSD 上的 posix_memalign 和 Windows 上的 aligned_alloc。对于使用英特尔 MKL 的人,有 mkl_malloc 和 mkl_free。
    猜你喜欢
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2018-08-08
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    相关资源
    最近更新 更多