#c #arrays #c #char #stdstring
#c #массивы #c #символ #stdstring
Вопрос:
Мне нужно добавить 64-разрядное число с плавающей запятой в unsigned char
массив по определенным индексам (например 1
, через индекс 8
).
Пример unsigned char
массива:
unsigned char msg[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Я хочу добавить число с плавающей запятой 0.084
, например, которое представлено 1B2FDD240681B53F
в виде шестнадцатеричного (с маленьким порядковым номером), в массив символов без знака по индексам 1,2,3,4,5,6,7,8
и оставить индексы 0
и 9
неизменными.
Итак, я хотел бы, чтобы массив символов без знака, msg
, содержал следующее:
msg = {0x00, 0x1B, 0x2F, 0xDD, 0x24, 0x06, 0x81, 0xB5, 0x3F, 0x00}
Пока я могу получить std::string
шестнадцатеричное представление примера значения с плавающей запятой 0.084
, используя следующий код, но я не уверен, как добавить строковые значения обратно в массив символов без знака:
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
int main()
{
union udoub
{
double d;
unsigned long long u;
};
double dVal = 0.084;
udoub val;
val.d = dVal;
std::stringstream ss;
ss << std::setw(16) << std::setfill('0') << std::hex << val.u << std::endl;
std::string strValHexString = ss.str();
cout<< strValHexString << std::endl;
return 0;
}
Вывод:
3fb5810624dd2f1b
Я попытался использовать std::copy
, как в примере ниже, для копирования значений из std::string
в unsigned char
, но, похоже, это не делает то, что я хочу:
unsigned char ucTmp[2];
std::copy(strValHexString.substr(0,2).begin(), strValHexString.substr(0,2).end(), ucTmp);
Ищу решение на C или C .
Комментарии:
1. @Adrian Mole: почему вы вообще думаете о рассмотрении чего-либо, кроме решения Adrian Mole?
2. @paulsm4 Зачем OP даже думать о рассмотрении сериализации в массив байтов, если они не выполняют хранение файлов или сетевую связь. В этих случаях решение Адриана Моула имеет проблему несовместимости между системами с разным порядковым номером. Это нормально, если оно ограничено одной системой.
3. Это для сетевого взаимодействия — изначально я думал, что мне придется изменить порядок, который я сделал перед использованием строк, но я считаю, что обе системы имеют небольшой конец, поэтому решение memcpy, похоже, работает нормально.
4. @user3716193
the memcpy solution seems to work fine
Это может показаться так, если вы тестируете только на одной системе или системах с одинаковым порядковым номером. Это не будет работать между системами с разным порядковым номером, что обычно является возможностью, которую следует учитывать при сетевом взаимодействии.5. Для автономного приложения беспокоиться о «порядковом порядке»… глупо. Однако для сетевого сообщения это, вероятно, ВАЖНО . Честно говоря, я не заметил, что имя переменной было «msg»; и при этом OP явно не сказал об этом в своем сообщении. «Сетевые сообщения» определенно требуют «более надежного» решения…
Ответ №1:
Форматирование байтов компонента в шестнадцатеричную строку, а затем повторное их чтение — ужасная трата времени и усилий. Просто используйте std::memcpy()
(в C ) или memcpy
(в C):
std::memcpy(amp;msg[1], amp;dVal, sizeof(dVal));
Это решит все необходимые проблемы с выравниванием указателей. Однако это не будет выполнять никакой «интерпретации» с точки зрения вашего порядкового номера — но это не должно быть проблемой, если вы затем не передаете этот массив байтов между разными платформами.
Комментарии:
1. По какой-то причине я застрял на мысли, что мне нужно добавить каждый байт отдельно, чтобы поместить их в правильный индекс в массив, моя ошибка. Я определенно полагал, что мой строковый метод был очень неэффективным. Это отлично работает, спасибо.
Ответ №2:
Ваш пример имеет неопределенное поведение из-за чтения из неактивного члена объединения. Четко определенный способ преобразования в целое число:
auto uVal = std::bit_cast<std::uint64_t>(dVal);
Теперь, когда у вас есть данные в виде целого числа, вы можете использовать побитовые операции для извлечения отдельных октетов в определенных позициях:
msg[1] = (uVal >> 0x0 ) amp; 0xff;
msg[2] = (uVal >> 0x8 ) amp; 0xff;
msg[3] = (uVal >> 0x10) amp; 0xff;
msg[4] = (uVal >> 0x18) amp; 0xff;
msg[5] = (uVal >> 0x20) amp; 0xff;
...
Это можно сжать в цикл.
Обратите внимание, что это работает одинаково независимо от порядкового номера процессора. Результирующий порядок в массиве всегда будет иметь малый порядковый номер, в отличие от прямого std::memcpy
подхода, который приводит к собственному порядковому порядку, который не обязательно имеет малый порядковый номер во всех системах. Однако, если числа с плавающей запятой и целые числа используют разный порядковый номер, то порядок не будет одинаковым даже при таком подходе.
Комментарии:
1. std::bit_cast недоступен для меня, я использую C 11.
2. @user3716193 Вы можете написать свое собственное
not_std::bit_cast
использованиеstd::memcpy
и использовать его вместо этого.3. В итоге я использовал std::memcpy. Поддержал этот ответ, хотя — я думаю, что это было направление, в котором я хотел пойти изначально — возможность использовать побитовые операции, потому что я использовал их раньше с целыми числами, я просто не мог понять, как это сделать с числами с плавающей запятой. Спасибо.