Допустимо ли преобразование uint16_t в uint8_t[2] с использованием приведения в C?

Я пытаюсь создать функцию, которая преобразует 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?

Напомним, что NATIVE_ENDIAN здесь должен относиться к порядку байтов целых чисел, в частности 2-байтовых целых чисел. Сегодня редко бывает, чтобы порядок байтов 4-байтовых целых чисел имел более двух вариантов, а порядок байтов значений FP может отличаться от целых чисел.

chux - Reinstate Monica 26.03.2024 15:07

@chux — Восстановить Монику. Исторически сложилось так, что да. Но мне было интересно, так ли это до сих пор. Использует ли что-нибудь новее, чем PDP-11, что-то кроме LE или BE?

ikegami 26.03.2024 15:08

Порядок байтов PDP довольно древний. Конец FP, не соответствующий целочисленному порядку байтов, все еще может встречаться в наши дни, хотя я не сталкивался с этим более 10 лет. Ключ в том, что ваша цель переносимости зависит от того, насколько хорошо код определяет endian и то, что не показано. IAC, C2X, скорее всего, представят __STDC_ENDIAN_LITTLE__, __STDC_ENDIAN_BIG__, __STDC_ENDIAN_NATIVE__, и любой код, написанный сегодня, должен учитывать, насколько хорошо он работает с ними.

chux - Reinstate Monica 26.03.2024 15:18

@chux-ReinstateMonica Однако по какой-то причине ни один компилятор/libc, похоже, еще не реализовал stdbit.h.

Lundin 26.03.2024 15:22

@Лундин Интересно. Планировать будущее C2X непросто. Я ожидал, что он выйдет в 2023 году, а теперь в 2024. Может, в 2025?

chux - Reinstate Monica 26.03.2024 15:34

@chux-ReinstateMonica ISO/IEC — это такая организация-динозавр. Готовый проект у них уже 1,5 года, а джека так и не добился. Поскольку колеса бюрократии работают так медленно, ISO/IEC просто больше не может использоваться для разработки технических стандартов. За это время человечество разработало и выпустило тысячи продвинутых микроконтроллеров с учетом C, но ISO не может даже опубликовать PDF-файл за это время... Мы не можем допустить, чтобы организация-динозавр так сильно засоряла технический прогресс, им нужно подлежит замене.

Lundin 26.03.2024 15:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Нет. Это может нарушить проблемы выравнивания и строгие требования к псевдонимам.

Используйте memcpy, сдвиги или союз. Хороший компилятор, если это возможно, оптимизирует это до простой инструкции перемещения.

memcpy( out, &in, 2 );

Спасибо за Ваш ответ! Вы можете проверить меня по этому поводу, но я думаю, что преобразование профсоюзов - это UB. Я не стал использовать >> из соображений скорости.

gberth 26.03.2024 15:14

Re: «Я не использовал >> из соображений скорости». Опять же, приличный компилятор, если возможно, оптимизирует это до простой инструкции перемещения. Никакого штрафа за скорость быть не должно.

ikegami 26.03.2024 15:14

@gberth "но я думаю, что преобразование профсоюзов - это UB". --. В C все в порядке, но для проверки необходимо просмотреть конкретный код.

chux - Reinstate Monica 26.03.2024 15:37
Ответ принят как подходящий

Использование приведения для этого нарушает строгие требования к псевдонимам.

Вместо этого вам следует использовать 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 26.03.2024 15:12

@gberth, это означает, что у вас могут возникнуть проблемы с выравниванием, как упоминалось в моем ответе. Некоторые среды могут адресовать только 16-битные значения, если их адрес делится на два.

ikegami 26.03.2024 15:13

@ikegami Строгое нарушение аласинга и проблема выравнивания — это не одно и то же. Строгое нарушение псевдонимов обычно приводит к тому, что компилятор выдает совершенно неверную последовательность инструкций, тогда как проблема с выравниванием обычно вызывает проблемы при чтении или записи ячейки памяти во время выполнения.

user694733 26.03.2024 15:17

@user694733 user694733 У вас причина и следствие поменялись местами. Строгого псевдонимов не существует, потому что компилятор будет генерировать мусор. Компилятору разрешено генерировать мусор из-за строгого псевдонимов. Строгий псевдоним существует из-за выравнивания.

ikegami 26.03.2024 15:17

@ikegami Нет. Строгое нарушение псевдонимов может произойти, даже если тип, используемый для доступа к объекту, имеет те же требования к выравниванию, что и объект, к которому осуществляется доступ.

user694733 26.03.2024 15:20

@ user694733, Конечно. И это не противоречит ничему из того, что я сказал. Последствия необратимы. Так что не «нет». В лучшем случае вы продемонстрировали, что может быть вторая причина существования строгого псевдонимов, что вполне возможно.

ikegami 26.03.2024 15:26

Требования к выравниванию @ikegami не имеют ничего общего с правилами псевдонимов, существует строгое псевдонимирование (как следует из названия), позволяющее компиляторам предполагать, что определенные значения (т. е. значения разных типов) не создают псевдонимы друг для друга. Это важно, потому что это позволяет компиляторам избегать создания избыточной загрузки/сохранения, без строгого псевдонимирования им пришлось бы доказывать, что любое значение, к которому осуществляется доступ, не является псевдонимом любого другого значения, к которому осуществляется доступ, чтобы иметь возможность сделать это.

Cubic 26.03.2024 15:27

@ikegami Строгое сглаживание существует из-за выравнивания.нехорошо. Однако похоже, что мы все согласны с тем, что использование (uint16_t*)out — это проблема.

chux - Reinstate Monica 26.03.2024 15:32

Что касается «строгого псевдонимов (как следует из названия), позволяющего компиляторам предполагать, что определенные значения (т.е. значения разных типов) не создают псевдонимы друг для друга». Да, мы оба знаем, что означает строгое псевдонимирование. Вопрос в том, почему он существует. /// Что касается «без строгого псевдонимирования им пришлось бы доказывать, что любое доступное значение не является псевдонимом какого-либо другого доступного значения». Им приходится делать это даже при строгом псевдониме. Потому что у нас могут быть псевдонимы. (q = (char*)p;, параметры указателя без restrict).

ikegami 26.03.2024 15:54

@ikegami: Если бы выравнивание было причиной правил псевдонимов, правила были бы такими: «Объект должен иметь доступ к сохраненному значению только с помощью выражения lvalue, требования к выравниванию которого, по крайней мере, столь же строги, как и выравнивание объекта». Они не. Основная причина применения правил псевдонимов заключается в том, что компилятор может лучше оптимизировать внутреннюю часть void foo(float *x, int *y) { … }, предполагая, что x и y указывают на разную память. Тот факт, что некоторые псевдонимы разрешены, как и в случае с типами символов, ограничивает полезность этого, но это остается целью правил.

Eric Postpischil 26.03.2024 16:04

@Eric Postpischil, Re «Если бы выравнивание было причиной правил псевдонимов», я просто сказал, что это причина. Я уже признал, что могут быть и другие. (Аргумент соломенного человека) [Сделал это более ясным в своем ответе]

ikegami 26.03.2024 16:10

@ikegami: Выравнивание не является причиной для правил псевдонимов, поскольку оно регулируется другими правилами, в частности C 2018 6.3.2.3 7. Правила псевдонимов будут иметь побочный продукт предотвращения (в смысле определения неопределенных) некоторых проблем несовпадения, но не всех. если бы не было других правил по этому поводу, но они не решают всех вопросов выравнивания и это не является их целью или причиной существования. Существуют и другие правила выравнивания, и их достаточно без правил псевдонимов. Правила псевдонимов не решают проблему выравнивания. Они решают проблему дополнительных оптимизаций компилятора.

Eric Postpischil 26.03.2024 16:12

Другие вопросы по теме