Ваша интуиция о том, «почему бы не использовать массив размером 1», очень точна.
Код неправильно выполняет "взлом структуры C", поскольку объявления массивов нулевой длины являются нарушением ограничения. Это означает, что компилятор может сразу же отклонить ваш хак во время компиляции с помощью диагностического сообщения, которое останавливает перевод.
Если мы хотим совершить хак, мы должны прокрасться мимо компилятора.
Правильный способ «взлома структуры C» (который совместим с диалектами C, начиная с ANSI C 1989 года и, возможно, намного раньше), заключается в использовании совершенно корректного массива размера 1:
struct someData
{
int nData;
unsigned char byData[1];
}
Кроме того, вместо sizeof struct someData
размер детали перед byData
рассчитывается по формуле:
offsetof(struct someData, byData);
Чтобы выделить struct someData
с пробелом для 42 байтов в byData
, мы бы тогда использовали:
struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);
Обратите внимание, что это offsetof
вычисление на самом деле является правильным вычислением даже в случае нулевого размера массива. Вы видите, sizeof
вся структура может включать отступы. Например, если у нас есть что-то вроде этого:
struct hack {
unsigned long ul;
char c;
char foo[0]; /* assuming our compiler accepts this nonsense */
};
Размер struct hack
вполне может быть дополнен для выравнивания из-за элемента ul
. Если unsigned long
имеет ширину в четыре байта, то вполне возможно, sizeof (struct hack)
равен 8, тогда как offsetof(struct hack, foo)
почти наверняка 5. Метод offsetof
- это способ получить точный размер предыдущей части структуры непосредственно перед массив.
Так что это был бы способ реорганизовать код: привести его в соответствие с классическим, легко переносимым хаком структуры.
Почему бы не использовать указатель? Потому что указатель занимает дополнительное место и должен быть инициализирован.
Есть и другие веские причины не использовать указатель, а именно то, что указатель требует адресного пространства, чтобы быть значимым. Хак с структурой доступен для внешнего применения: то есть существуют ситуации, в которых такая компоновка соответствует внешнему хранилищу, например областям файлов, пакетов или разделяемой памяти, в которых вам не нужны указатели, поскольку они не имеют смысла.
Несколько лет назад я использовал struct hack в интерфейсе передачи сообщений совместно используемой памяти между ядром и пользовательским пространством. Я не хотел указателей там, потому что они были бы значимы только для исходного адресного пространства процесса, генерирующего сообщение. Часть ядра программного обеспечения видела память, используя свое собственное отображение по другому адресу, и поэтому все было основано на вычислениях смещения.