Предполагая, что unsigned int
не имеет ловушечных представлений, выполните одно или оба из приведенных ниже выражений (A) и (B), спровоцируйте поведение undefined, почему или почему нет, и (особенно, если вы считаете, что один из них хорошо -defined, но другой нет), считаете ли вы, что дефект в стандарте? Меня в первую очередь интересует текущая версия стандарта C (т.е. C2011), но если это отличается в старых версиях стандарта или на С++, я также хотел бы узнать об этом.
(_Alignas
используется в этой программе, чтобы исключить любой вопрос UB из-за неадекватного выравнивания. Однако правила, которые я обсуждаю в своей интерпретации, не говорят о выравнивании.)
#include <stdlib.h>
#include <string.h>
int main(void)
{
unsigned int v1, v2;
unsigned char _Alignas(unsigned int) b1[sizeof(unsigned int)];
unsigned char *b2 = malloc(sizeof(unsigned int));
if (!b2) return 1;
memset(b1, 0x55, sizeof(unsigned int));
memset(b2, 0x55, sizeof(unsigned int));
v1 = *(unsigned int *)b1; /* (A) */
v2 = *(unsigned int *)b2; /* (B) */
return !(v1 == v2);
}
Моя интерпретация C2011 заключается в том, что (A) вызывает поведение undefined, но (B) хорошо определен (для хранения неопределенного значения в v2
), потому что:
-
memset
определяется (§7.24.6.1) для записи в свой первый аргумент as-if через lvalue с типом символа, который разрешен как дляb1
, так иb2
для специального случая в нижняя часть §6.5p7. -
Объект
b1
имеет объявленный типunsigned char[n]
. Поэтому его эффективный тип для доступа такжеunsigned char[n]
на 6.5p6. Оператор (A) читаетb1
через выражение lvalue, тип которогоunsigned int
, который не является эффективным типомb1
или любым из других исключений в 6.5p7, поэтому поведение undefined. -
Объект, на который указывает
b2
, не имеет объявленного типа. Значение, сохраненное в нем (черезmemset
), было (как-если) через lvalue с типом символа, поэтому второй случай 6.5p6 не применяется. Значение не было скопировано нигде, поэтому третий случай 6.5p6 также не применяется. Поэтому эффективным типом объекта является тип lvalue, используемого для доступа, который равенunsigned int
, и выполняются правила 6.5p7. -
Наконец, в соответствии с 6.2.6.1, если
unsigned int
не имеет ловушечных представлений, операцияmemset
создала представление некоторого неопределенного значенияunsigned int
в каждом изb1
иb2
. Поэтому, если ни (A), ни (B) не провоцируют поведение undefined, то фактические значения вv1
иv2
не определены, но они равны.
Комментарий:
Асимметрия правил "наложения на основе типа" (т.е. 6.5p7), разрешающая доступ к объекту с любым эффективным типом доступа с помощью символьного типа, но не наоборот, является постоянным источником путаницы, Второй случай 6.5p6, кажется, был добавлен специально, чтобы предотвратить его поведение undefined для чтения значения, инициализированного memset
(или, если на то пошло, calloc
), но, поскольку оно применимо только к объектам без объявленный тип, сам по себе является дополнительным источником путаницы.