Реализация Memcpy, строгое сглаживание

Изучая c, я реализовал свои собственные функции memcpy. Я использовал более широкий тип (uint32_t) в функции. (Для простоты функция ограничена типами, кратными 4, и данные правильно выровнены)

void memcpy4( void* dst , void* src , int size )
{
    size /= 4;

    for ( int i = 0 ; i < size ; i++ )
        ((uint32_t*)dst)[i] = ((uint32_t*)src)[i];
}

Я кое-что прочитал о каламбурах и строгом использовании псевдонимов, и я считаю, что приведенная выше функция нарушает правило. Правильная реализация будет такой, поскольку вы можете использовать char:

void memcpy4( void* dst , void* src , int size )
{
    for ( int i = 0 ; i < size ; i++ )
        ((char *)dst)[i] = ((char *)src)[i];
}

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

Как реализовать такую ​​функцию с более широким типом и не нарушить строгое правило алиасинга?


person this    schedule 19.01.2014    source источник
comment
size в байтах? Если это так, вам не нужно будет использовать союз; пусть функция memcpy4 выполняет копирование, и пусть другие функции обрабатывают свои данные по-своему... Для более широкого типа обратите внимание, что int count=size/sizeof(uint32_t); должно быть достаточно для первой функции.   -  person abiessu    schedule 19.01.2014
comment
@abiessu Размер в байтах. Я упростил функцию для удобства чтения. Конечно, в полной реализации есть проверка на это.   -  person this    schedule 19.01.2014
comment
@PaulR разделить на 2 - это опечатка. Раньше это был правый сдвиг.   -  person this    schedule 19.01.2014
comment
Извините, опять чек на что? Кроме того, вы можете просто #define MY_IMPLEMENTED_COPY_TYPE uint32_t сделать int count=size/sizeof(MY_IMPLEMENTED_COPY_TYPE);, а затем ((MY_IMPLEMENTED_COPY_TYPE*)dst)[i] = ((MY_IMPLEMENTED_COPY_TYPE*)src)[i];   -  person abiessu    schedule 19.01.2014
comment
Поскольку это обучающее упражнение, позвольте мне предложить вам еще одну вещь: Никогда не используйте знаковые целые числа для размеров и индексов. Используйте целые числа без знака или лучше std::size_t. Такая реализация memcpy() является классическим примером атаки на основе подписанного целого числа.   -  person Manu343726    schedule 19.01.2014
comment
size /= 4 по-прежнему опасен. Подсказка: sizeof — ваш друг.   -  person Bob Jarvis - Reinstate Monica    schedule 19.01.2014
comment
@BobJarvis Это функция memcpy, как я могу сделать sizeof?, информации о типе нет.   -  person this    schedule 19.01.2014
comment
Ваша реализация использует uint32_t. Насколько большой uint32_t? Я не знаю — я знаю, что я мог бы ДОПОЛНИТЬ, но я не ЗНАЮ — и я абсолютно не знаю ни на одной из платформ. Попробуйте size /= sizeof(uint32_t).   -  person Bob Jarvis - Reinstate Monica    schedule 19.01.2014
comment
Итак, вы говорите, что в первом примере нет строгой проблемы с псевдонимами?   -  person this    schedule 19.01.2014
comment
@PaulR: в другом месте ОП сказал, что это функция, разработанная специально для данных, размер которых кратен 4.   -  person abiessu    schedule 19.01.2014
comment
@BobJarvis uint32_t хорошо определен, и его размер равен 4 на каждой платформе. Я имею в виду действительно..   -  person this    schedule 19.01.2014
comment
@себя. Я не думаю, что кто-то говорит, что нет строгой проблемы с псевдонимами. Чтение, например. 6.5:6 и 6.5:7 в стандарте C99, ваш код определенно нарушает строгое сглаживание при вызове функции, если только она не вызывается для копирования массивов uint32_t.   -  person Pascal Cuoq    schedule 19.01.2014
comment
Правильная реализация должна иметь дело с тем фактом, что рассматриваемые указатели (как источник, так и пункт назначения) могут быть не выровнены по отношению к любым границам, которые могут быть важны для конкретной архитектуры. Я знаю, что это просто упражнение, но я призываю вас сесть и справиться со всеми пограничными условиями. Вот как человек учится.   -  person Bob Jarvis - Reinstate Monica    schedule 19.01.2014
comment
sizeof(uint32_t) обычно равно 4, но может быть меньше на некоторых платформах, где CHAR_BIT > 8.   -  person Paul R    schedule 19.01.2014
comment
@self.: uint32_t определяется как 32 бита, а не 4 байта. Нет требования, чтобы байт был 8-битным, и есть множество платформ, где это не так.   -  person Mike Seymour    schedule 19.01.2014
comment
@MikeSeymour Спасибо. Можете ли вы назвать несколько?   -  person this    schedule 19.01.2014
comment
@self.: Архитектуры в стиле DSP (например, Motorola 56k, TI TMS) являются наиболее распространенными в наши дни - они обычно имеют размер слова, достаточно большой для данных, для которых они предназначены (16, 24, 32,... бит) без поддержки адресных блоков меньшего размера. В прошлом архитектуры мэйнфреймов и суперкомпьютеров (например, PDP-11, Cray) имели схожие ограничения; сегодня это редкость, но я не сомневаюсь, что все еще существуют специализированные суперкомпьютеры. И невозможно сказать, сохранят ли будущие архитектуры особенности сегодняшних популярных процессоров.   -  person Mike Seymour    schedule 19.01.2014


Ответы (1)


Способ реализации memcpy с использованием более чем однобайтовых копий заключается в использовании нестандартного C.

Стандарт C не поддерживает реализацию memcpy с использованием символов, отличных от типов.

Реализации Quality C предоставляют оптимизированную реализацию memcpy, которая выполняет эффективное копирование, используя более чем однобайтовые копии, но для этого они используют код, специфичный для реализации. Они могут сделать это, скомпилировав реализацию memcpy с переключателем, таким как -fnostrict-aliasing, чтобы сообщить компилятору, что правила псевдонимов будут нарушены в коде, полагаясь на известные особенности конкретной реализации C, чтобы гарантировать, что код будет работать (если вы напишете компилятор, вы можете спроектировать его так, чтобы ваша реализация memcpy работала), или написав memcpy на ассемблере.

Кроме того, реализации C могут оптимизировать вызовы memcpy там, где они появляются в исходном коде, заменяя их прямыми инструкциями для выполнения операции или просто изменяя внутреннюю семантику программы. (Например, если вы скопируете a в b, компилятор может вообще не выполнять копирование, а просто загрузиться из a, где последующий код обращается к b.)

Чтобы реализовать собственную специализированную операцию копирования с нарушением правил псевдонимов, скомпилируйте ее с помощью -fnostrict-aliasing, если вы используете GCC или Clang. Если вы используете другой компилятор, проверьте в его документации возможность отключения правил псевдонимов. (Примечание: Apple GCC, который я использую, по умолчанию отключает строгие псевдонимы и принимает -fstrict-aliasing, но не -fnostrict-aliasing. Я предполагаю, что не-Apple GCC принимает -fnostrict-aliasing.)

Если вы используете хорошую реализацию C, вы можете обнаружить, что ваша четырехбайтовая копия реализации memcpy4 не работает так же хорошо, как нативная memcpy, в зависимости от обстоятельств.

person Eric Postpischil    schedule 19.01.2014