Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Использование массива нулевого размера

#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 эту возможность поддерживают. Насколько я понимаю, массивы нулевого размера так и остались компиляторным расширением и в стандарт не вошли.

Комментариев нет:

Отправить комментарий