Переполнение буфера с помощью битовых полей и инициализация значения - ошибка компилятора или неопределенное поведение? - программирование
Подтвердить что ты не робот

Переполнение буфера с помощью битовых полей и инициализация значения - ошибка компилятора или неопределенное поведение?

Итак, я столкнулся со странной ошибкой на работе, когда пытался уменьшить размер структуры с помощью битовых полей. Мне удалось изолировать проблему и создать минимальную версию, которая повторяет проблему. Это дало дополнительное преимущество: MSVC теперь даже предупреждает о том, что он собирается делать: Ссылка на Compiler Explorer. Однако у Clang и GCC нет проблем с этим кодом.

#include <new>

enum foo : unsigned char { zero, one };

struct S
{
    int a{ 42 }; //Intentionally not trivial
    foo b : 1;
    foo c : 1;
};

auto test()
{
    constexpr auto size      = sizeof (S);
    constexpr auto alignment = alignof(S);

    struct {
        alignas(alignment) unsigned char bytes[size];
    } data;

    //Buffer overrun on this line
    ::new(static_cast<void*>(&data)) S{};

    //Just to avoid optimizing away the offending instructions
    return data;
}

Буфер, который я использую, должен подходить для хранения объекта, так как он имитирует std::aligned_storage, и я вызываю True Placement New, чтобы сохранить в нем свой объект. Я считаю, что так работает f.ex std::vector. Несмотря на это, я получаю это предупреждение:

предупреждение C4789: буфер 'data' размером 8 байт будет переполнен; 5 байтов будут записаны, начиная со смещения 4

Странная вещь в том, что проблема исчезнет, если фигурные скобки будут заменены круглыми скобками (хотя все равно должно быть инициализировано значение, верно?) Или просто удалены полностью (инициализировано по умолчанию).

Проблема также исчезнет, если S тривиален. Кроме того, проблема возникает только с включенными оптимизациями (из-за этого вы получили удовольствие от отладки).

Я не верю, что я вызываю неопределенное поведение здесь, но альтернатива заключается в том, что есть ошибка в VS 2017 и 2019. В настоящее время я работаю над этой проблемой, просто не используя фигурную инициализацию и придерживаясь скобок, где я подозреваю, что могут быть проблемы, но это не так.

Почему это происходит, и что я могу сделать, чтобы не беспокоиться о бомбах замедленного действия в моем коде? Переключение на другой компилятор не вариант.

Обновление: Итак, посмотрев немного больше на сборку, я вижу, что она по-прежнему генерирует подозрительный код при использовании скобок для инициализации значения. Только инициализация по умолчанию производит ожидаемую сборку. Кажется довольно странным, и я определенно подозреваю ошибку компилятора, но я бы предпочел еще кое-что об этом.

4b9b3361

Ответ 1

Эта ошибка компилятора исправлена в VS 2019 16.5.

В качестве обходного пути для тех, кто не может выполнить обновление, рассмотрите возможность замены скобок с регулярными скобками, например, замените S{...}; на S(...);. Если аргументы не передаются конструктору, рассмотрите возможность простого удаления фигурных скобок, поскольку объект по-прежнему создается по умолчанию.