Может ли эта memcpy привести к неопределенному поведению? - PullRequest
0 голосов
/ 17 января 2019

При таком определении:

struct vector {
    const float x;
    const float y;
};

может ли приведенный ниже фрагмент кода привести к неопределенному поведению?

struct vector src = {.x=1.0, .y=1.0};
struct vector dst;
void *dstPtr = &dst;    
memcpy(dstPtr, &src, sizeof dst);

gcc и clang не выдают никаких предупреждений, ноэто приводит к модификации константного типа.

Конструкция очень похожа на приведенную в принятом ответе на Как инициализировать константные элементы структур в куче , чтопо-видимому, соответствует.Я не понимаю, как мой пример был бы несовместимым.

1 Ответ

0 голосов
/ 18 января 2019

Квалификаторы const для членов позволяют компилятору предполагать, что - после инициализации объекта - эти члены не должны быть изменены каким-либо образом, и он может соответствующим образом оптимизировать код (см., Например, комментарий @Ajay Brahmakshatriya ).

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

Я думаю, что есть главное различие между вашим примером и тем, что в принятом ответе вы привели. В ответе this SO целевой агрегатный объект с константными типами элементов создается с помощью malloc:

ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);

В соответствии с правилами доступа к сохраненному значению объекта (см. Эту часть онлайнового стандартного черновика c ), целевой объект p получит свой эффективный тип впервые в ходе исполнения memcpy; эффективный тип - это тип исходного объекта init, а первый memcpy объекта, получившего malloced, можно рассматривать как инициализацию. Однако впоследствии изменение константных членов целевого объекта будет UB (я думаю, что даже секунда memcpy будет UB, но это, вероятно, основано на мнении).

6,5 выражений

  1. Эффективным типом объекта для доступа к его сохраненному значению является объявленный тип объекта, если таковой имеется.87) ... Если значение равно скопировать в объект, не имеющий объявленного типа, используя memcpy или memmove, или копируется как массив символьного типа, тогда эффективный тип измененный объект для этого доступа и для последующих обращений, которые не изменять значение - это эффективный тип объекта, из которого значение копируется , если оно есть. Для всех других доступов к объект не имеет объявленного типа, эффективный тип объекта просто тип lvalue, используемый для доступа.

87) Выделенные объекты не имеют объявленного типа.

Однако в вашем примере целевой объект dst уже имеет объявленный тип в своем определении struct vector dst;. Следовательно, квалификаторы const на элементах dst уже существуют до применения memcpy, и его следует рассматривать как назначение, а не как инициализацию.

Так что я бы проголосовал за UB в этом случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...