Таким образом, C99 благословил обычно используемый хак с «гибким элементом массива», позволив нам сделать struct
s, которые могут быть перераспределены в соответствии с нашими требованиями к размеру. Я подозреваю, что это вполне безопасно на большинстве вменяемых реализаций, чтобы сделать это, но законно ли в C "отменить выделение", если мы знаем в определенных ситуациях, что нам не понадобятся некоторые члены struct
?
Абстрактный пример
Скажите, что у меня есть тип:
struct a {
bool data_is_x;
void * data;
size_t pos;
};
Если data_is_x
, то тип data
- это тип, который должен использовать член pos
. В противном случае функциям, которые работают с этим struct
, не понадобится член pos
для этой конкретной копии struct
. По сути, struct
несет в себе информацию о том, есть ли у него pos
член, и эта информация не будет изменена в течение жизни struct
(за исключением злого вреда, который все равно в значительной степени сломает) , Можно ли с уверенностью сказать:
struct a *a = malloc(data_is_x ? sizeof(struct a) : offsetof(struct a, pos));
который выделит место для pos
члена, только если он нужен? Или это нарушает ограничение на использование пространства приведения, которое слишком мало для указателя struct
, даже если вы никогда не используете рассматриваемые элементы?
Конкретный пример
Мой реальный пример использования немного сложен; это здесь в основном, чтобы вы могли понять почему Я хочу сделать это:
typedef struct {
size_t size;
void * data;
size_t pos;
} mylist;
Код для mylist_create
указывает, что для size > 0
, data
- это массив непрерывных данных, длина которого size
элементов (независимо от того, какой элемент может быть), но для size == 0
это текущий узел двусвязного списка, содержащего элементы. Все функции, которые работают с mylist
s, проверят, является ли size == 0
. Если это так, они будут обрабатывать данные в виде связанного списка с «текущим» индексом, на который указывает узел data
. Если нет, они будут обрабатывать данные как массив с «текущим» индексом, хранящимся в pos
.
Теперь, если size == 0
, нам на самом деле не нужен pos
член, но если size > 0
, мы будем нуждаться. Поэтому мой вопрос: законно ли это делать:
mylist *list = malloc(size ? sizeof(mylist) : offsetof(mylist, pos));
Если мы гарантируем (на штраф за неопределенное поведение), что, в то время как size == 0
, мы никогда не будем пытаться (или должны) получить доступ к члену pos
? Или где-то в стандарте сказано, что UB даже думать об этом?