Предоставляет ли какой-либо компилятор эффективный тип передачи через memcpy / memmove

Согласно N1570 6.5 / 6:

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

Это предполагает, что даже в системе, где «long» и какой-либо другой целочисленный тип имеют одинаковое представление, следующее вызывает Undefined Behavior:

#if ~0UL == ~0U #define long_equiv int #elif ~0UL == ~0ULL #define long_equiv long long #else #error Oops #endif long blah(void) { long l; long_equiv l2; long_equiv *p = malloc(sizeof (long)); l = 1234; memcpy(p, &l, sizeof (long)); l2 = *p; free(p); // Added to address complaint about leak return l2; } 

поскольку данные, на которые указывает l имеют эффективный тип long а объект, на который указывает p не имеет объявленного типа, memcpy должен установить long хранения эффективного типа хранилища. Поскольку чтение значения lvalue типа long_equiv для чтения объекта с эффективным типом long не допускается, код будет вызывать Undefined Behavior.

Учитывая, что до C99 memcpy был одним из стандартных способов копирования данных одного типа для хранения другого типа, новые правила memcpy приводят к тому, что многие существующие коды вызывают Undefined Behavior. Если бы в этом правиле было указано, что использование memcpy для записи в выделенное хранилище оставляет место назначения без какого-либо эффективного типа, поведение будет определено.

Существуют ли какие-либо компиляторы, которые не ведут себя так, как если бы memcpy эффективный тип адресата при использовании для копирования информации в выделенное хранилище или же использование memcpy для целей перевода данных считалось «безопасным»? Если некоторые компиляторы действительно применяют эффективный тип источника к месту назначения, каков будет правильный способ копирования данных в агностическом стиле? Что означает «скопировано как массив типа символа»?

В стандарте C говорится, что эффективный тип передается. Поэтому, по определению, все соответствующие компиляторы передают эффективный тип.

Ваш образец кода вызывает неопределенное поведение, нарушая правило строгого сглаживания, потому что значение эффективного типа long читается lvalue типа long long .

Это также верно в C89, я не уверен, что вы называете «новыми правилами на C99» (кроме того, что long long не было на C89).

Это правда, что когда C стандартизован, некоторый существующий код имел неопределенное поведение. И также верно, что люди продолжают писать код с неопределенным поведением.

Что означает «скопировано как массив типа символа»?

Это означает, что вы копируете персонаж по типу символа.

каков был бы правильный способ копирования данных в агностической форме?

Насколько мне известно, невозможно «стереть эффективный тип». Чтобы правильно прочитать значение с использованием long long * , вы должны указывать на место эффективного типа long long .

В вашем коде, например:

 // If we have confirmed that long and long long have the same size and representation long long x; memcpy(&x, p, sizeof x); return x; 

Союзный псевдоним – еще один вариант.

Если вам не нравится все это, тогда скомпилируйте с -fno-strict-aliasing .

Экспериментально gcc 6.2 ведет себя так, как это было бы оправдано, если рассматривать memmove как перенос эффективного типа источника в пункт назначения. Если gcc может определить, что указатели источника и адресата совпадают, он будет обрабатывать операнд памяти как только читаемый через свой более ранний эффективный тип, а не как память, которая была в последний раз написана с использованием типа символа, и поэтому к нему можно получить доступ с использованием любого типа. Такое поведение было бы неоправданным без правила, которое позволяет memcpy передавать информацию об эффективном типе.

С другой стороны, поведение gcc иногда не может быть оправдано ни при каких правилах, поэтому не обязательно ясно, является ли поведение gcc следствием интерпретации его авторов Стандартом или его просто сломано. Например, если он может определить, что целевая цель memcpy содержит один и тот же шаблон константного бита в качестве источника, он будет обрабатывать memcpy как no-op, даже если источник сохранил тип, который затем будет использоваться для чтения целевого хранилища , и назначение, которое было назначено другим типом, компилятор решил не мог читать следующий текст.