Я пытаюсь создать функцию, которая преобразует uint16_t в uint8_t[2] в C.
Следующий код был протестирован, но даже после прочтения дизассемблирования я не уверен, что он переносим или безопасен.
static inline uint8_t* write_u16(uint16_t in, uint8_t out[2], ArchEndian_t endian) {
if (NATIVE_ENDIAN != endian){
in = byte_swap(in);
}
*((uint16_t*)out) = in; //< Is this portable and safe?
return out;
}
Как мне следует относиться к приведению типов в C?
@chux — Восстановить Монику. Исторически сложилось так, что да. Но мне было интересно, так ли это до сих пор. Использует ли что-нибудь новее, чем PDP-11, что-то кроме LE или BE?
Порядок байтов PDP довольно древний. Конец FP, не соответствующий целочисленному порядку байтов, все еще может встречаться в наши дни, хотя я не сталкивался с этим более 10 лет. Ключ в том, что ваша цель переносимости зависит от того, насколько хорошо код определяет endian и то, что не показано. IAC, C2X, скорее всего, представят __STDC_ENDIAN_LITTLE__, __STDC_ENDIAN_BIG__, __STDC_ENDIAN_NATIVE__, и любой код, написанный сегодня, должен учитывать, насколько хорошо он работает с ними.
@chux-ReinstateMonica Однако по какой-то причине ни один компилятор/libc, похоже, еще не реализовал stdbit.h.
@Лундин Интересно. Планировать будущее C2X непросто. Я ожидал, что он выйдет в 2023 году, а теперь в 2024. Может, в 2025?
@chux-ReinstateMonica ISO/IEC — это такая организация-динозавр. Готовый проект у них уже 1,5 года, а джека так и не добился. Поскольку колеса бюрократии работают так медленно, ISO/IEC просто больше не может использоваться для разработки технических стандартов. За это время человечество разработало и выпустило тысячи продвинутых микроконтроллеров с учетом C, но ISO не может даже опубликовать PDF-файл за это время... Мы не можем допустить, чтобы организация-динозавр так сильно засоряла технический прогресс, им нужно подлежит замене.





Нет. Это может нарушить проблемы выравнивания и строгие требования к псевдонимам.
Используйте memcpy, сдвиги или союз. Хороший компилятор, если это возможно, оптимизирует это до простой инструкции перемещения.
memcpy( out, &in, 2 );
Спасибо за Ваш ответ! Вы можете проверить меня по этому поводу, но я думаю, что преобразование профсоюзов - это UB. Я не стал использовать >> из соображений скорости.
Re: «Я не использовал >> из соображений скорости». Опять же, приличный компилятор, если возможно, оптимизирует это до простой инструкции перемещения. Никакого штрафа за скорость быть не должно.
@gberth "но я думаю, что преобразование профсоюзов - это UB". --. В C все в порядке, но для проверки необходимо просмотреть конкретный код.
Использование приведения для этого нарушает строгие требования к псевдонимам.
Вместо этого вам следует использовать memcpy следующим образом:
#include <string.h>
#include <stdint.h>
static inline uint8_t *write_u16(uint16_t in, uint8_t out[2], ArchEndian_t endian) {
if (NATIVE_ENDIAN != endian) {
in = byte_swap(in);
}
return memcpy(out, &in, sizeof(in));
}
Спасибо за Ваш ответ! Есть ли у вас справочное предложение, чтобы я мог лучше понять, что вы подразумеваете под строгим нарушением псевдонимов?
@gberth, это означает, что у вас могут возникнуть проблемы с выравниванием, как упоминалось в моем ответе. Некоторые среды могут адресовать только 16-битные значения, если их адрес делится на два.
@ikegami Строгое нарушение аласинга и проблема выравнивания — это не одно и то же. Строгое нарушение псевдонимов обычно приводит к тому, что компилятор выдает совершенно неверную последовательность инструкций, тогда как проблема с выравниванием обычно вызывает проблемы при чтении или записи ячейки памяти во время выполнения.
@user694733 user694733 У вас причина и следствие поменялись местами. Строгого псевдонимов не существует, потому что компилятор будет генерировать мусор. Компилятору разрешено генерировать мусор из-за строгого псевдонимов. Строгий псевдоним существует из-за выравнивания.
@ikegami Нет. Строгое нарушение псевдонимов может произойти, даже если тип, используемый для доступа к объекту, имеет те же требования к выравниванию, что и объект, к которому осуществляется доступ.
@ user694733, Конечно. И это не противоречит ничему из того, что я сказал. Последствия необратимы. Так что не «нет». В лучшем случае вы продемонстрировали, что может быть вторая причина существования строгого псевдонимов, что вполне возможно.
Требования к выравниванию @ikegami не имеют ничего общего с правилами псевдонимов, существует строгое псевдонимирование (как следует из названия), позволяющее компиляторам предполагать, что определенные значения (т. е. значения разных типов) не создают псевдонимы друг для друга. Это важно, потому что это позволяет компиляторам избегать создания избыточной загрузки/сохранения, без строгого псевдонимирования им пришлось бы доказывать, что любое значение, к которому осуществляется доступ, не является псевдонимом любого другого значения, к которому осуществляется доступ, чтобы иметь возможность сделать это.
@ikegami Строгое сглаживание существует из-за выравнивания.нехорошо. Однако похоже, что мы все согласны с тем, что использование (uint16_t*)out — это проблема.
Что касается «строгого псевдонимов (как следует из названия), позволяющего компиляторам предполагать, что определенные значения (т.е. значения разных типов) не создают псевдонимы друг для друга». Да, мы оба знаем, что означает строгое псевдонимирование. Вопрос в том, почему он существует. /// Что касается «без строгого псевдонимирования им пришлось бы доказывать, что любое доступное значение не является псевдонимом какого-либо другого доступного значения». Им приходится делать это даже при строгом псевдониме. Потому что у нас могут быть псевдонимы. (q = (char*)p;, параметры указателя без restrict).
@ikegami: Если бы выравнивание было причиной правил псевдонимов, правила были бы такими: «Объект должен иметь доступ к сохраненному значению только с помощью выражения lvalue, требования к выравниванию которого, по крайней мере, столь же строги, как и выравнивание объекта». Они не. Основная причина применения правил псевдонимов заключается в том, что компилятор может лучше оптимизировать внутреннюю часть void foo(float *x, int *y) { … }, предполагая, что x и y указывают на разную память. Тот факт, что некоторые псевдонимы разрешены, как и в случае с типами символов, ограничивает полезность этого, но это остается целью правил.
@Eric Postpischil, Re «Если бы выравнивание было причиной правил псевдонимов», я просто сказал, что это причина. Я уже признал, что могут быть и другие. (Аргумент соломенного человека) [Сделал это более ясным в своем ответе]
@ikegami: Выравнивание не является причиной для правил псевдонимов, поскольку оно регулируется другими правилами, в частности C 2018 6.3.2.3 7. Правила псевдонимов будут иметь побочный продукт предотвращения (в смысле определения неопределенных) некоторых проблем несовпадения, но не всех. если бы не было других правил по этому поводу, но они не решают всех вопросов выравнивания и это не является их целью или причиной существования. Существуют и другие правила выравнивания, и их достаточно без правил псевдонимов. Правила псевдонимов не решают проблему выравнивания. Они решают проблему дополнительных оптимизаций компилятора.
Напомним, что
NATIVE_ENDIANздесь должен относиться к порядку байтов целых чисел, в частности 2-байтовых целых чисел. Сегодня редко бывает, чтобы порядок байтов 4-байтовых целых чисел имел более двух вариантов, а порядок байтов значений FP может отличаться от целых чисел.