Ваше внимание к теме «почему бы не использовать массив размера 1» находится на месте.
Неправильный код «C struct hack», потому что объявления массивов с нулевой длиной являются нарушением ограничения. Это означает, что компилятор может отказаться от вашего взлома с места в пути во время компиляции с помощью диагностического сообщения, которое останавливает перевод.
Если мы хотим совершить взлом, мы должны прокрасть его мимо компилятора.
Правильный способ сделать «C STRUCT хак» (который совместим с C диалекты вернуться к 1989 ANSI C, и, вероятно, гораздо раньше), чтобы использовать совершенно действительный массив размером 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 в обмениваемой памяти, передавая интерфейс между ядром и пользовательским пространством. Мне не нужны указатели, потому что они были бы значимыми только для исходного адресного пространства процесса, генерирующего сообщение. Ядро части программного обеспечения имело представление о памяти, используя его собственное сопоставление на другом адресе, и поэтому все было основано на расчетах смещения.
Это «структурный взлом», описанный в вопросе 2.6 [comp.lang.c FAQ] (http://www.c-faq.com/). Деннис Ритчи назвал это «необоснованным chumminess с реализацией C». C99 представила новую языковую функцию - «гибкий элемент массива», чтобы заменить структурный хак. Даже компилятор Microsoft, который отличается отсутствием поддержки C99, поддерживает гибкие элементы массива. – 2012-09-11 18:37:11
НЕ добавляйте тег `c` к этому вопросу.Правила C++ для этого сильно отличаются от правил C. – 2014-05-21 06:03:52
@BenVoigt Принятый ответ - это чистый код C, поэтому, я думаю, ваше редактирование неверно. c hack применяется как для c, так и для C++ таким же образом – 2014-05-21 06:14:23