Это не неопределенное поведение , независимо от того , что говорят официальные лица или кто- либо другой , потому что оно определено стандартом. p->s, за исключением случаев использования в качестве lvalue, вычисляет указатель, идентичный (char *)p + offsetof(struct T, s). В частности, это действительный charуказатель внутри объекта malloc'd, и есть 100 (или более, в зависимости от соображений выравнивания) последовательных адресов, следующих сразу за ним, которые также действительны как charобъекты внутри выделенного объекта. Тот факт, что указатель был получен путем использования ->вместо явного добавления смещения к указателю, возвращаемому функцией malloccast to char *, не имеет значения.
Технически, p->s[0]это единственный элемент charмассива внутри структуры, следующие несколько элементов (например, p->s[1]сквозные p->s[3]), вероятно, являются байтами заполнения внутри структуры, которые могут быть повреждены, если вы выполняете присваивание структуре в целом, но не если вы просто обращаетесь к отдельным члены, а остальные элементы - это дополнительное пространство в выделенном объекте, которое вы можете использовать, как хотите, при условии, что вы соблюдаете требования выравнивания (и charне имеете требований к выравниванию).
Если вас беспокоит, что возможность перекрытия байтов заполнения в структуре может каким-то образом вызвать назальных демонов, вы можете избежать этого, заменив 1in [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.