Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 5.00/18: Рейтинг темы: голосов - 18, средняя оценка - 5.00
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602

Strict aliasing и memcpy

07.06.2017, 00:48. Показов 4209. Ответов 36
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Что-то я никак не врублюсь, как согласуются между собой strict aliasing и функции жонглирующие void*? Возьмем для примера memcpy. Стандартная оптимизация данной функции - копировать данные не отдельными байтами, а кусками побольше. Например, int-ами. Но для этого надо залезть через int* указатель в данные, которые вообще говоря могут и не являться массивом int. А strict aliasing такие фокусы запрещает. Однако, функция в стандарте есть. Так как она тогда в этот стандарт вписывается?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void*myMemcpy(void*dst,const void*src,std::size_t size)
{
    int*dstInt=reinterpret_cast<int*>(dst);
    const int*srcInt=reinterpret_cast<const int*>(src);
    for(;size>=sizeof(int);size-=sizeof(int))
        //строго следуя букве стандарта, здесь у нас UB
        //ведь никто не сказал что по адресу srcInt действительно лежит int
        *dstInt++=*srcInt++;
 
    char*dstChar=reinterpret_cast<char*>(dstInt);
    const char*srcChar=reinterpret_cast<const char*>(srcInt);
    for(;size;--size)
        *dstChar++=*srcChar++;
    return dst;
}
Ну и выдержка из стандарта:
If a program attempts to access the stored value of an object through a glvalue of other than one of the
following types the behavior is undefined: 52
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type similar (as defined in 4.4) to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
of the object,
— an aggregate or union type that includes one of the aforementioned types among its elements or non-
static data members (including, recursively, an element or non-static data member of a subaggregate
or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
07.06.2017, 00:48
Ответы с готовыми решениями:

Каламбур типизации и strict aliasing
Есть ли какой-то стандартный способ обойти strict aliasing, с гарантией от UB? Конкретная задача: дан массив чисел. Требуется отсортировать...

Memcpy, buffer overflow. Может ли возникнуть ошибка в функции memcpy
Бывает ли на практике такое, что код #define size 1000; // some value int x, y; /* ... */ memcpy(y, x, (size + 1) * sizeof(int)); ...

Union, new placement, strict-aliasing, cross-platform
Доброго времени суток. Ниже представленный код вроде бы работает. Гонял его на компиляторах cl/mingw ...

36
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12932 / 6800 / 1820
Регистрация: 18.10.2014
Сообщений: 17,211
07.06.2017, 03:18
Цитата Сообщение от Renji Посмотреть сообщение
Но для этого надо залезть через int* указатель в данные, которые вообще говоря могут и не являться массивом int.
Во-первых, концептуально, функции типа memcpy переинтерпретируют исходные объекты, как массивы unsigned char. Это прямо разрешено правилами strict aliasing.

Во-вторых, фактически реализация стандартной библиотеки может делать что угодно. На нее не распространяются никакие требования и ограничения стандарта языка. Стандартная библиотека реализуется на платформенно-зависимом языке, который не имеет никакого отношения к языкам С или С++. Любые внешние сходства - случайны. Это относится даже к стандартным заголовочным файлам, не говоря уже о файлах реализации стандартных функций.

Поэтому применять правила strict aliasing к непосредственной платформенно-зависимой реализации memcpy - бессмысленно. Они на нее не распространяются.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
07.06.2017, 03:35  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
О каком еще int * указателе идет речь?
Речь идет о указателе через который из источника будет извлекаться больше одного байта за раз. Интерпретировать источник как массив char шибко накладно по времени.
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Стандартная библиотека реализуется на платформенно-зависимом языке, который не имеет никакого отношения к языкам С или С++.
А я хочу свою реализацию, с хедер-онли и инлайнами. Неужели только на платформенно-зависимом языке делать?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12932 / 6800 / 1820
Регистрация: 18.10.2014
Сообщений: 17,211
07.06.2017, 08:59
Цитата Сообщение от Renji Посмотреть сообщение
А я хочу свою реализацию, с хедер-онли и инлайнами. Неужели только на платформенно-зависимом языке делать?
Подобное у себя? Да, вам такое не позволено. Стандартной библиотеке - можно, а вам - нельзя. Поэтому если вам позарез хочется делать что-то подобное - то только путем использования внутренних специфических свойств реализации, которые бы гарантировали вам, что компилятор тут ничего не наоптимизирует из соображений strict aliasing.
1
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
07.06.2017, 09:45
Цитата Сообщение от Renji Посмотреть сообщение
Неужели только на платформенно-зависимом языке делать?
Тут смысл такой. Когда ты как пользователь пишешь программу, то дальше на неё натравливаешь компилятор миллион раз. И чтобы компилятор работал правильно в любых условиях и с любыми опциями, нужно, чтобы текст программы удовлетворял стандарту. Тело системной функции memcpy поставляется в виде единожды скомпилированного бинарника и больше стадию компиляции не проходит. Т.е. независимо от внешних условий и опций сам по себе бинарный код memcpy не изменяется, т.е. будет всегда работать одинаково.

Если ты хочешь свою реализацию memcpy, то тут условно три варианта:
1. Ты пишешь её на языке с соблюдением всех стандартов и включаешь в своё проект в виде исходника. В этом случае возможна только медленная реализация (о чём ты писал в первом абзаце в посте #3)
2. Ты пишешь её на языке абы как, лишь бы скомпилировалось правильно на конкретной версии компилятора с конкретными опциями и в конкретном окружении. Дальше подключаешь в свой проект в виде бинарника и больше не перекомпилируешь
3. Полностью пишешь на ассемблере и подключаешь к проекту в виде исходника на ассемблере
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
07.06.2017, 12:38  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Подобное у себя? Да, вам такое не позволено. Стандартной библиотеке - можно, а вам - нельзя.
Окей, тогда я хочу свой WriteFile. Тоже нельзя?
Цитата Сообщение от Evg Посмотреть сообщение
Тело системной функции memcpy поставляется в виде единожды скомпилированного бинарника и больше стадию компиляции не проходит. Т.е. независимо от внешних условий и опций сам по себе бинарный код memcpy не изменяется, т.е. будет всегда работать одинаково.
Если строго формально, то нет, даже бинарный код одинаково работать не будет. Цепочка для сбоя такая:
Вот у нас переменная var типа double. В целях оптимизации ее значение лежит в регистре.
Вот у нас вызов memcpy. Мы точно знаем что внутри у него никаких указателей на double нет.
Если в memcpy нет указателей на double, значит по стандарту она никак не может прочитать нашу var. Ну вот пусть var тогда и дальше в регистре лежит.
Но тут опаньки, в memcpy уезжает указатель на var. И какие-то данные memcpy конечно читает, да только не те что у нас в регистре закешированы.

Реально то да, компилятор пока не достаточно умен для таких трюков. Но формально имеет полное право.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
07.06.2017, 14:51
Цитата Сообщение от Renji Посмотреть сообщение
Вот у нас переменная var типа double. В целях оптимизации ее значение лежит в регистре.
Вот у нас вызов memcpy
Переменная, лежащая на регистре не может попасть в memcpy. А если попадает, то она перекочёвывает в память (в стек). Компилятор достаточно умный для того, чтобы увидеть вызов memcpy и заменить его на копирование из регистра в регистр. Но при этом он не пытается применить всякие оптимизации, базирующиеся на типе указателя, вокруг этого места (и специальной пометкой для этого является наличие memcpy). В отличие от случая, когда копирование было бы сделано на языке без использование memcpy

Добавлено через 1 минуту
Важным моментом является то, что при наличии функции my_memcpy, не факт, что компилятор такую оптимизацию проведёт
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
07.06.2017, 15:11  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Переменная, лежащая на регистре не может попасть в memcpy. А если попадает, то она перекочёвывает в память (в стек).
Так в memcpy попадает не переменная, а ее адрес. Который вы по стандарту разыменовать все равно не можете (потому что в memcpy нет double*/char*). Раз разыменовать вы не можете, почему компилятор должен хранить по этому адресу актуальные данные?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
07.06.2017, 15:57
Цитата Сообщение от Renji Посмотреть сообщение
оторый вы по стандарту разыменовать все равно не можете
Если я правильно понимаю, о чём ты говоришь, то ты не можешь его разыменовывать в своей программе. Про системную функцию memcpy, как выше сказал TheCalligrapher, речь не идёт

Ну и вообще, как ты себе представляешь передачу куда бы то ни было, адрес того, что лежит в регистре?
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
07.06.2017, 16:42
Цитата Сообщение от Renji Посмотреть сообщение
Если строго формально, то нет, даже бинарный код одинаково работать не будет.
Он в танке. То что сказано в стандарте о strict aliasing rules violation касается оптимизаций компилятора. Есть даже ключи которые могут в ряде случаев снять проблему, но снизят скорость. А бинарник уже не будет компилироваться. В нём не могут происходить оптимизации. Ведь оптимизация это и есть лиса/орёл/суслик которые делают всё в противоположность написанному коду. Без них всё хорошо.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
07.06.2017, 17:53  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Ну и вообще, как ты себе представляешь передачу куда бы то ни было, адрес того, что лежит в регистре?
Элементарно.
1) Создаем переменную var по адресу 0x12345678.
2) Пытаемся увеличить var на единицу, для чего переносим ее в регистр. Ибо складывать числа непосредственно в памяти процессор не умеет (экзотические умеют, но не про них речь).
3) Результат обратно в память не выгружаем, потому что экономим время.
4) Передаем в memcpy адрес var. То есть, 0x12345678. Данные по этому адресу давно протухли, но мы полагаем что функция следует стандарту и в данные не полезет.
Цитата Сообщение от Evg Посмотреть сообщение
Если я правильно понимаю, о чём ты говоришь, то ты не можешь его разыменовывать в своей программе. Про системную функцию memcpy, как выше сказал TheCalligrapher, речь не идёт
Поправка: "даже бинарный код одинаково работать не будет", если его не обшить специальными ключевыми словами, отключающими фокусы выше. Так то да, в gcc есть атрибут __may_alias__ позволяющий обойти проблему. Не переносимый, правда.
0
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
07.06.2017, 18:52
Что-то я никак не врублюсь, как согласуются между собой strict aliasing и функции жонглирующие void*? Возьмем для примера memcpy.
memcpy это intrinsic... ассемблер ж, какие там касты?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
07.06.2017, 19:27
Цитата Сообщение от Renji Посмотреть сообщение
4) Передаем в memcpy адрес var. То есть, 0x12345678. Данные по этому адресу давно протухли, но мы полагаем что функция следует стандарту и в данные не полезет.
Ты описываешь модель неработающего компилятора или что?

Не по теме:

Цитата Сообщение от IGPIGP Посмотреть сообщение
Он в танке
Действительно

0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
07.06.2017, 20:27  [ТС]
Цитата Сообщение от tapochka Посмотреть сообщение
memcpy это intrinsic... ассемблер ж, какие там касты?
То-то я смотрю, если mingw скормить ключик -nostdlib, memcpy волшебным образом отваливается, а компилятор матерится на неразрешенный внешний символ.
Нет, оно нифига не intrinsic, а самая обычная функция из стандартной библиотеки.

Добавлено через 1 минуту
Цитата Сообщение от Evg Посмотреть сообщение
Ты описываешь модель неработающего компилятора или что?
Я описываю потенциальную реакцию компилятора на UB. Еще раз, стандарт явно запрещает лезть в double переменную через int* указатель. Если вы все же этого делаете, компилятор имеет полное право творить любой треш и угар.
0
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
08.06.2017, 00:49
Renji, попробуйте всобачить это(взято с просторов инета):

C++
1
2
3
4
5
6
опции компилятора: -O2 -fno-builtin -nostdlib 
 
#undef memcpy
#define bos0(dest) __builtin_object_size (dest, 0)
#define memcpy(dest, src, n) \
  __builtin___memcpy_chk (dest, src, n, bos0 (dest))
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
08.06.2017, 03:18  [ТС]
Цитата Сообщение от tapochka Посмотреть сообщение
Renji, попробуйте всобачить это(взято с просторов инета):
Спасибо конечно, но mingw 4.9.2 - ошибка: undefined reference to `__memcpy_chk'. В g++ той же версии под Линуксом та же петрушка.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
08.06.2017, 13:05
Цитата Сообщение от tapochka Посмотреть сообщение
memcpy это intrinsic... ассемблер ж, какие там касты?
Вероятно, ты глядишь на gcc и делаешь глобальные выводы. А в gcc по умолчанию включен режим -fbuiltin, который большинство стандартных функций превращает в builtin'ы. В данном случае вызов функции memcpy превращается в __builtin_memcpy, который в случаях небольших размеров вместо вызова функции делает встроенный код (что в конечном итоге позволяет сделать копирование с регистра не регистр). Подай опцию -fno-builtin и пропадёт щястье.

У других компиляторов, вероятно, есть похожие технологии
2
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
08.06.2017, 21:47
В данном случае вызов функции memcpy превращается в __builtin_memcpy, который в случаях небольших размеров вместо вызова функции делает встроенный код
да я вот не пойму почему вызов функции всегда не превращается в __builtin_memcpy? какой смысл вызывать memcpy как функцию?
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
09.06.2017, 03:23  [ТС]
Цитата Сообщение от tapochka Посмотреть сообщение
да я вот не пойму почему вызов функции всегда не превращается в __builtin_memcpy? какой смысл вызывать memcpy как функцию?
Потому что четыре байта можно скопировать одной машинной инструкцией, а сорок четыре нельзя. А когда машинных инструкций много, появляется смысл вынести их в отдельную функцию.
1
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
09.06.2017, 09:45
Для меня тема strict aliasing это вещь непонятная. Работа с сырой памятью - дело выбора каждого. Какие оптимизации могут этому мешать? В чём их цель? То есть, я имею в виду вот что. Когда новичок пишет:
C++
1
2
3
4
void foo(){
int a,b;
a=b;
}
компилятор вправе игнорировать такой вызов. На основании того, что так будет быстрее и компактнее, а результат тот же. Тут нет вопросов.
А вот когда компилятор может "оптимизировать" операцию записи в память, это уже не смешно. Почему создатели компилятора считают, что нужно кастрировать обезопасить язык таким чудом. Имеет ли смысл писать компилятор для новичков и дурачков с тем, чтобы напичкать его горой подгузников?
Я смогу относиться к strict aliasing с пониманием, только когда увижу адекватный код, в котором такая оптимизация уместна. Если у кого-то есть примеры, -покажите пожалуйста.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
09.06.2017, 09:45
Помогаю со студенческими работами здесь

Оптимизация кода, нарушающего правила strict aliasing
Доброго времени суток. Имеется такой код в ядре Linux: /* include/linux/netlink.h */ .... #define NETLINK_CB(skb) ...

warning: dereferencing pointer 't' does break strict-aliasing rules
Здравствуйте, уважаемые специалисты! К чему может привести данное предупреждение? Что нужно сделать, чтобы его не было? typedef...

SkyBox Anti-Aliasing
Здравствуйте Вот смотрю неплохую ссылочку. Все хорошо, понятно написано. Смущает вот это color = texture(skybox, R);Ну или вместо R...

Имитация anti-aliasing фотошопа средствами CSS
Граждане верстальщики, подскажите, есть ли средства в CSS сделать текст таким, как во-втором варианте? Изображение

Anti-aliasing Asus Rog Strix RX-480-08G Gaming
Всем привет! Всплыла проблема не корректной работы режимов сглаживания для видеокарты Asus Rog Strix RX-480-08G Gaming в некоторых...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит переходные токи и напряжения на элементах схемы. . . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru