Я пытался понять, насколько законно приведенное ниже, и мне действительно нужна помощь.
#include <stdio.h>
#include <stdlib.h>
typedef struct foo {
int foo;
int bar;
} foo;
void make_foo(void * p)
{
foo * this = (foo *)p;
this->foo = 0;
this->bar = 1;
}
typedef struct more_foo {
int foo;
int bar;
int more;
} more_foo;
void make_more_foo(void * p)
{
make_foo(p);
more_foo * this = (more_foo *)p;
this->more = 2;
}
int main(void)
{
more_foo * mf = malloc(sizeof(more_foo));
make_more_foo(mf);
printf("%d %d %d\n", mf->foo, mf->bar, mf->more);
return 0;
}
Насколько я понял, это является каламбуром типов и, как предполагается, нарушает строгие правила псевдонима. Но есть ли это? Переданные указатели недействительны. Вы можете интерпретировать указатель void как хотите, верно?
Кроме того, я читал, что могут быть проблемы с выравниванием памяти. Но выравнивание структуры детерминировано. Если начальные члены одинаковы, то они будут выровнены таким же образом, и не должно возникнуть проблем с доступом ко всем членам foo из указателя more_foo. Это верно?
GCC компилируется с -Wall без предупреждений, программа работает как положено. Однако я не уверен, UB это или нет и почему.
Я также видел, что это:
typedef union baz {
struct foo f;
struct more_foo mf;
} baz;
void some_func(void)
{
baz b;
more_foo * mf = &b.mf; // or more_foo * mf = (more_foo *)&b;
make_more_foo(mf);
printf("%d %d %d\n", mf->foo, mf->bar, mf->more);
}
вроде разрешено. Из-за полиморфной природы объединений компилятор с этим согласится. Это верно? Означает ли это, что при компиляции со строгим отключением псевдонимов вам не нужно использовать объединение и вместо этого можно использовать только структуры?
Изменить: union baz
теперь компилируется.
union baz
недействителен и не компилируется. Пожалуйста исправьте. - person Paul Ogilvie   schedule 13.02.2018make_foo(p); more_foo * this = (more_foo *)p; this->more = 2;
, поскольку компилятор может игнорировать, чтоp
иthis
указывают на перекрывающиеся данные. Таким образом, порядок оценкиmake_foo(p);
иmore_foo * this = (more_foo *)p; this->more = 2;
может происходить либо в порядке, либо одновременно. Теперь компилятор мог подумать, что он может использоватьint
доступ в 2 раза кthis
и перезаписать то, что сделалmake_foo(p);
. Звучит надумано, но это АА. - person chux - Reinstate Monica   schedule 13.02.2018void*
может быть изменен на указатель символа или на его исходный тип указателя. Но если указатель изначально был адресомint
, он не может быть скрытым доstruct foo*
. Я не думаю, что эта проблема сводит на нет ваше расследование. - person chux - Reinstate Monica   schedule 13.02.2018int
доступ шириной 2x? Ширинаint
известна. Даже если порядок оценки меняется, элементы не перекрываются в памяти. - person Vlad Dinev   schedule 14.02.2018*this
и*p
не перекрываются. Предположим, что компилятор может иметь доступ к быстрой 128-битной инструкции чтения и читать сразу весь*this
в регистр. (Компилятор также дополнилstruct more_foo
до 128 бит.) Включаетthis->more = 2;
в регистр и записывает его обратно в*this
. В то же самое времяmake_foo(p);
происходило. Порядок этих двух неопределен и UB на AA - насколько я понимаю. - person chux - Reinstate Monica   schedule 14.02.2018struct *
закодирован так же, как и другиеstruct *
, и они указывают на данные в той же области памяти, которая имеет общее требование выравнивания.char
объекты могут существовать где-то еще с другим выравниванием и представлением указателя. То же самое дляdouble
объект может существовать где-то еще. - person chux - Reinstate Monica   schedule 14.02.2018make_more_foo
не будет нарушения SA, поскольку единственные преобразования - в / изvoid *
. Ему присваиваетсяvoid *
, он переходит к другой функции, ожидающейvoid *
, затем преобразует его вmore_foo *
и разыменовывает. - person dbush   schedule 22.03.2018