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