В коде ядра Linux я обнаружил следующее, чего не могу понять.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Код здесь: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Для чего нужен массив данных с нулевыми элементами?
c
structure
flexible-array-member
Джигар Патель
источник
источник
Ответы:
Это способ иметь переменные размеры данных без необходимости дважды вызывать
malloc
(kmalloc
в данном случае). Вы бы использовали это так:Раньше это было нестандартно и считалось взломом (как сказал Аникет), но оно было стандартизировано в C99 . Стандартный формат для этого сейчас:
Обратите внимание, что вы не указываете размер
data
поля. Также обратите внимание, что эта специальная переменная может находиться только в конце структуры.В C99 этот вопрос объясняется в 6.7.2.1.16 (выделено мной):
Или другими словами, если у вас есть:
Вы можете получить доступ
var->data
с помощью индексов в[0, extra)
. Обратите внимание, чтоsizeof(struct something)
это дает только размер, учитывающий другие переменные, т.е. даетdata
размер 0.Также может быть интересно отметить, как стандарт фактически дает примеры создания
malloc
такой конструкции (6.7.2.1.17):Еще одно интересное примечание стандарта в том же месте (выделено мной):
источник
[0, extra)
?Фактически, это взлом для GCC ( C90 ).
Это также называется взломом структуры .
Поэтому в следующий раз я бы сказал:
Это будет эквивалентно высказыванию:
И я могу создать любое количество таких структурных объектов.
источник
Идея состоит в том, чтобы разрешить массив переменного размера в конце структуры. По- видимому,
bts_action
некоторые пакетные данные с заголовком фиксированного размера (Thetype
иsize
полей), и переменного размераdata
элемента. Объявив его как массив нулевой длины, он может быть проиндексирован так же, как любой другой массив. Затем вы выделяетеbts_action
структуру размером, скажем, 1024 байтаdata
, например:См. Также: http://c2.com/cgi/wiki?StructHack
источник
malloc
заставляет вас повторяться несколько раз, и если когда-либо типaction
изменений, вы должны исправить это несколько раз. Сравните следующие два для себя, и вы поймете:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
Второй короче, чище и, очевидно, легче изменить.Код недействителен C ( см. Это ). Ядро Linux по очевидным причинам нисколько не заботится о переносимости, поэтому в нем используется много нестандартного кода.
То, что они делают, - это нестандартное расширение GCC с размером массива 0. Была бы написана соответствующая стандарту программа,
u8 data[];
и это означало бы то же самое. Авторы ядра Linux, по-видимому, любят делать вещи излишне сложными и нестандартными, если появится возможность сделать это.В более старых стандартах C завершение структуры пустым массивом было известно как «взлом структуры». Другие уже объяснили его цель в других ответах. Взлом структуры в стандарте C90 имел неопределенное поведение и мог вызвать сбои, в основном потому, что компилятор C может добавлять любое количество байтов заполнения в конец структуры. Такие байты заполнения могут конфликтовать с данными, которые вы пытались «взломать» в конце структуры.
GCC рано сделал нестандартное расширение, чтобы изменить это поведение с неопределенного на четко определенное. Затем стандарт C99 адаптировал эту концепцию, и поэтому любая современная программа C может использовать эту функцию без риска. Он известен как C99 / C11 гибкий элемент массива .
источник
Еще одно использование массива нулевой длины - это именованная метка внутри структуры для помощи при проверке смещения структуры во время компиляции.
Предположим, у вас есть несколько больших определений структур (охватывающих несколько строк кэша), которые вы хотите убедиться, что они выровнены по границе строки кеша как в начале, так и в середине, где она пересекает границу.
В коде вы можете объявить их, используя расширения GCC, например:
Но вы все же хотите убедиться, что это выполняется во время выполнения.
Это сработает для одной структуры, но будет сложно охватить несколько структур, каждая из которых имеет разные имена членов, которые нужно выровнять. Скорее всего, вы получите код, подобный приведенному ниже, где вам нужно найти имена первого члена каждой структуры:
Вместо этого вы можете объявить в структуре массив нулевой длины, действующий как именованная метка с согласованным именем, но не занимающий места.
Тогда будет намного проще поддерживать код утверждения времени выполнения:
источник