Переполнение буфера с помощью битовых полей и инициализация значения - ошибка компилятора или неопределенное поведение?
Итак, я столкнулся со странной ошибкой на работе, когда пытался уменьшить размер структуры с помощью битовых полей. Мне удалось изолировать проблему и создать минимальную версию, которая повторяет проблему. Это дало дополнительное преимущество: 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. В настоящее время я работаю над этой проблемой, просто не используя фигурную инициализацию и придерживаясь скобок, где я подозреваю, что могут быть проблемы, но это не так.
Почему это происходит, и что я могу сделать, чтобы не беспокоиться о бомбах замедленного действия в моем коде? Переключение на другой компилятор не вариант.
Обновление:
Итак, посмотрев немного больше на сборку, я вижу, что она по-прежнему генерирует подозрительный код при использовании скобок для инициализации значения. Только инициализация по умолчанию производит ожидаемую сборку. Кажется довольно странным, и я определенно подозреваю ошибку компилятора, но я бы предпочел еще кое-что об этом.
Ответы
Ответ 1
Эта ошибка компилятора исправлена в VS 2019 16.5.
В качестве обходного пути для тех, кто не может выполнить обновление, рассмотрите возможность замены скобок с регулярными скобками, например, замените S{...};
на S(...);
. Если аргументы не передаются конструктору, рассмотрите возможность простого удаления фигурных скобок, поскольку объект по-прежнему создается по умолчанию.