Это не неопределенное поведение , независимо от того, что кто-либо, официальный или иным образом , говорит, потому что оно определено стандартом.p->s
, за исключением случаев использования в качестве lvalue, оценивает указатель, идентичный (char *)p + offsetof(struct T, s)
.В частности, это действительный указатель char
внутри объекта malloc, и сразу после него есть 100 (или более, зависящих от соображений выравнивания) последовательных адресов, которые также действительны как char
объекты внутри выделенного объекта.Тот факт, что указатель был получен с помощью ->
вместо явного добавления смещения к указателю, возвращенному malloc
, приведенному к char *
, не имеет значения.
Технически, p->s[0]
является единственнымэлемент массива char
внутри структуры, следующие несколько элементов (например, p->s[1]
- p->s[3]
), вероятно, являются байтами заполнения внутри структуры, которые могут быть повреждены, если вы выполняете присваивание структуре в целом, но не есливы просто получаете доступ к отдельным элементам, а остальные элементы - это дополнительное пространство в выделенном объекте, которое вы можете использовать по своему усмотрению, если вы подчиняетесь требованиям выравнивания (а char
не имеет требований выравнивания).
Если вы обеспокоены тем, что возможность перекрытия байтов заполнения в структуре может каким-то образом вызывать носовых демонов, вы можете избежать этого, заменив 1
в [1]
значением, гарантирующим отсутствие заполнения вконец структуры.Простой, но расточительный способ сделать это - создать структуру с одинаковыми элементами, кроме массива в конце, и использовать s[sizeof struct that_other_struct];
для массива.Затем p->s[i]
четко определяется как элемент массива в структуре для i<sizeof struct that_other_struct
и как объект типа char по адресу, следующему за концом структуры для i>=sizeof struct that_other_struct
.
Редактировать: На самом деле, в приведенном выше приеме для получения правильного размера вам может также понадобиться поместить объединение, содержащее каждый простой тип, перед массивом, чтобы гарантировать, что сам массив начинается с максимального выравнивания, а не в середине какого-либо другогозаполнение элемента.Опять же, я не верю, что все это необходимо, но я предлагаю это для самого параноика из языковых юристов.
Редактировать 2: Перекрытие сЗаполнение байтов определенно не является проблемой из-за другой части стандарта.C требует, чтобы, если две структуры согласуются в начальной подпоследовательности их элементов, к общим начальным элементам можно получить доступ через указатель на любой тип.Как следствие, если бы была объявлена структура, идентичная struct T
, но с большим конечным массивом, элемент s[0]
должен был бы совпадать с элементом s[0]
в struct T
, и присутствие этих дополнительных элементов не могло бы бытьповлиять или повлиять на доступ к общим элементам большей структуры с помощью указателя на struct T
.