#cpp #visual_cpp
Меня всегда интересовало, почему компилятор ругается на эту ошибку и как ее исправить. struct A{ int size; unsigned char data[0]; }; warning C4200: нестандартное расширение: массив нулевого размера в структуре (объединении) Используется эта структура так: A *a = (A*) malloc(1024); a->size = 1024 - sizeof(A); Нулевой размер используется для того, чтобы sizeof возвращала только размер структуры, без лишних данных. Для исправления ошибки можно убрать переменную data, но тогда будет сложнее обращаться к ней. Использовать unsigned char *data, тоже не вариант, так как придется либо выделять новый блок памяти, либо указывать в data на область памяти сразу за структурой. Вот и получается, что это предупреждение никак не исправить. Или все же есть варианты?
Ответы
Ответ 1
Компилятор ругается потому, массивы нулевого размера запрещены стандартом. Некоторые компиляторы разрешают это, но не все. Проблема с массивами нулевого размера такая же, как и с любыми объектами нулевого размера: адреса разных объектов должны быть разными. Это (неявное) предположение используется большим количеством кода. Имея объекты нулевого размера, легко нарушить это правило. Для случая массива переменного размера в конце структуры используется так называемый struct hack, на который вам уже указывали. В нём массив объявляется просто как array[], без указания размера. Это и есть ответ на вопрос, как правильно исправить: struct A{ int size; unsigned char data[]; }; Это решение, однако, требует компиляции языком C, А не C++, как правильно отметил @ixSci. Visual Studio (по крайней мере 2013-ая) компилирует код как C++. Несмотря на то, что в стандарте C++ такого нет, этот код компилируется, но выдаётся предупреждение, что не может быть создан копирующий конструктор. Это и правильно, так как правильный копирующий конструктор невозможен. Вы можете подавить это предупреждение при помощи #pragma warning(suppress : 4200) struct A { int size; unsigned char data[]; }; Не забудьте, что правильное использование структуры со flexible array member'ом — вычисление требуемого размера в рантайме и выделение её через malloc с указанием правильного размера.Ответ 2
Массивы нулевого размера запрещены потому что размер всех объектов должен быть больше единицы, чтобы у каждого объекта был свой уникальный адрес. Для решения Вашей задачи обычно используют следующий код: struct A { int size; }; unsigned char* data(A* a) { return reinterpret_cast(a + 1); } A* create_A(int size) { A* a = (A*)malloc(size + sizeof(A)); a->size = size; return a; } В случае, если нужен массив из элементов, тип которых отличается от [unsigned] char, надо учитывать выравнивание. Для этого надо использовать спецификатор alignas: template struct alignas(T) A { std::size_t size; T* data() { return reinterpret_cast (this + 1); }; }; Использование массива из одного элемента, чтобы хранить произвольное количество элементов, например struct A { int size; unsigned char data[1]; }; не рекомендуется, т.к. статические анализаторы будут выдавать ошибки, если увидят чтение за концом массива. Ответ 3
Начиная со стандарта C99, для этого правильно использовать flexible array member: struct f1 { int x; int y[]; } f1 = { 1, { 2, 3, 4 } }; Современные версии MSVC эту возможность поддерживают. Насколько я понимаю, массивы нулевого размера так и остались компиляторным расширением и в стандарт не вошли.
Комментариев нет:
Отправить комментарий