Делает ли заполнение структуры C это использование небезопасным?

Предположим, у меня есть структура, будь то объединение или иное:

    typedef struct {
        union {
            struct { float x, y, z; } xyz;
            struct { float r, g, b; } rgb;
            float xyz[3];
        } notAnonymous;
    } Vector3;

Я слышал, что некоторые компиляторы автоматически дополняют структуры для повышения производительности, создавая границы, выровненные по словам.

Предположительно такая синергия означает, что размер структуры не может быть гарантированно равен сумме размеров полей ее компонентов, и поэтому существует изменение повреждения данных и/или переполнения для массива xyzs в следующем:

inline Vector3 v3Make(float x, float y, float z) { Vector3 v = {x,y,z}; return v; }
float xyzs[6];
*(Vector3*)&xyzs[3] = v3Make(4.0f,5.0f,6.0f);
*(Vector3*)&xyzs[0] = v3Make(1.0f,2.0f,3.0f);

Правильный?


person KomodoDave    schedule 11.01.2012    source источник


Ответы (4)


Это правда, что компилятор может добавить в вашу структуру любое дополнение, которое ему нужно. Вы можете использовать #pragma pack или __attribute__((packed)), чтобы избежать заполнения в большинстве компиляторов. На практике у вас есть три 32-битных поля, так что, вероятно, это не будет проблемой. Вы можете проверить, используя sizeof для вашего типа структуры или переменной этого типа и посмотреть, что получится.

Что является проблемой, так это то, что вы пытаетесь присвоить Vector3 переменной float в последних двух строках. Этого не допустят. Вы можете взломать то, что пытаетесь сделать:

*(Vector3 *)&xyzs[3] = v3Make(4.0f, 5.0f, 6.0f);

Но выглядит это довольно уродливо, не говоря уже о том, что это сбивает с толку. Было бы намного лучше изменить xyzs на массив Vector3, а не только float.

person Carl Norum    schedule 11.01.2012
comment
Что касается __attribute((packed)), см. stackoverflow. ком/вопросы/8568432/ - person Keith Thompson; 12.01.2012
comment
Согласен - не забывайте перестраховываться! - person Carl Norum; 12.01.2012
comment
Глупый я - да, конечно, мне нужен актерский состав, я изменил свой код, чтобы отразить то, что вы сказали. Запутанный характер не влияет на мой вопрос, и большая часть кода GL может показаться запутанной, это больше касается функциональности, чем эстетики imo. Также хороший момент о 32-битных полях в моем примере - я должен был пойти на что-то более бесформенное. Пометка как выбранный ответ за точность ваших комментариев и практическую значимость подсказки #pragma. Большое спасибо! - person KomodoDave; 12.01.2012

См. ответы на вопросы в отчете о дефектах C № 074.

http://www.open-std.org/jtc1/sc22/wg14/docs/rr/dr_074.html

person ouah    schedule 11.01.2012
comment
Из-за сугубо технического характера связанного документа я проголосовал за этот пост, но не выбрал его в качестве предпочтительного ответа. - person KomodoDave; 12.01.2012

Это небезопасно по своей сути, компилятор/компоновщик позаботится обо всех смещениях.

Если... вы передаете структуру другой программе, написанной на другом языке или в другой системе, или другой программе, написанной на том же языке в той же системе, но с другими настройками компилятора. Тогда смещения могут быть рассчитаны неправильно.

person Steve Wellens    schedule 11.01.2012

В соответствии со стандартом C это, по крайней мере, определяется реализацией (зависит от проблем с заполнением) и, возможно, поведение undefined (из-за правил псевдонимов?), но на всех реальных компиляторах это будет работать так, как ожидалось. Выравнивание типа никогда не может быть больше, чем его размер (оно всегда делит размер типа поровну), и только патологически плохой компилятор будет вставлять в структуры дополнительное заполнение сверх того, что необходимо для заполнения каждого члена до правильного выравнивания для его типа.

С учетом сказанного, по крайней мере, в моей книге, такой вид хака является необоснованным вызовом неопределенного поведения ради синтаксического уксуса и неприемлем. Если вам когда-нибудь понадобится получить доступ к данным в виде массива, просто всегда используйте форму массива. Гораздо проще помнить, что компоненты вектора всегда v[0], v[1] и v[2], чем помнить, что v[1] и rgb.g могут ссылаться на один и тот же объект в памяти...

person R.. GitHub STOP HELPING ICE    schedule 12.01.2012