首页 > 解决方案 > 使用较小的默认对齐重载新运算符

问题描述

C++17为过度对齐的数据引入了动态内存分配

除了现有std::max_align_t的基本对齐之外,它还添加__STDCPP_DEFAULT_NEW_ALIGNMENT__了 operator new 保证的最小对齐。

使用 MSVC2017 64 位编译,这些常量导致std::max_align_t大小为 8 和__STDCPP_DEFAULT_NEW_ALIGNMENT__大小为 16。

但是,如cppreference: operator new - global replacements中所述,它可以否决 operator new/free 。

查看所有文档,我不清楚是否允许此函数提供新的默认对齐方式,如果允许,是否允许我们重新定义此常量。

举例说明:

#include <new>
#include <iostream>
#include <cassert>
#include <cstdint>
#include <cstddef>

static_assert(alignof(std::max_align_t) == 8);
static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16);


void * operator new(size_t size) 
{ 
    std::cout << "New operator overloading " << std::endl; 
    void * p = std::malloc((size == 8) ? 16 : size); 
    assert(std::uintptr_t(p)%16 == 0);
    if (size == 8)
        {
        auto correctedPtr = std::uintptr_t(p) + 8;
        return (void*)correctedPtr;
        }
    return p; 
} 

void operator delete(void * p) 
{ 
    std::cout << "Delete operator overloading " << std::endl; 
    if (std::uintptr_t(p)%16 != 0)
    {
        auto correctedPtr = std::uintptr_t(p) - 8;
        std::free((void*)correctedPtr);
    }
    std::free(p); 
}

namespace
{
    struct D
    {
        double d;
    };
}


int main(int, char**)
{
    new D{};
    return 0;
}

编译器资源管理器中的代码

我问这个的原因是因为我正在调查一个现在用 Clang 编译的 MSVC 程序中的崩溃。在这里我们注意到,clang 使用依赖于这种 16 位对齐的 CPU 指令来初始化一个大小为 8 的类。

标签: c++language-lawyerc++17

解决方案


根据N4659(C++17 的最新公开草案):

6.7.4p3:

C++ 程序中定义的任何分配和/或解除分配函数,包括库中的默认版本,应符合 6.7.4.1 和 6.7.4.2 中指定的语义。

6.7.4.1p2:

...返回的指针应适当对齐,以便可以将其转换为指向任何合适的完整对象类型(21.6.2.1)的指针,然后用于访问已分配存储中的对象或数组(直到存储被显式释放通过调用相应的释放函数)。...

19.8p1:

以下宏名称应由实现定义: ... __STDCPP_DEFAULT_NEW_ALIGNMENT__std::size_t 类型的整数文字,其值是调用 operator new(std::size_t)or保证的对齐operator new[](std::size_t)。...

19.8p4:

如果本子条款中的任何预定义宏名称或标识符defined是 a#define#undef 预处理指令的主题,则行为未定义。...

因此,您无法更改__STDCPP_DEFAULT_NEW_ALIGNMENT__程序中的值,并且如果您的分配函数被调用为alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__)大小为 8 的类型,您无法检测到这一点,但您仍然需要返回一个适当对齐的指针。

尽管如此,您可以使用编译器选项更改__STDCPP_DEFAULT_NEW_ALIGNMENT__clang 本身定义的值。-fnew-alignment不确定它是否对您的情况有帮助。


推荐阅读