2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу 1, 2  След.
 
 Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 16:15 
Аватара пользователя
Вопрос, наверное, банальный (так как задача банальная), но красивого решения не знаю. :roll:

Есть необходимость обратиться к переменным типов int, double int, float как к массиву байт - прочитать и записать по-байтно.
Известные мне методы связаны либо с конвертацией тем или иным способом переменной в строку, или через шифты
но для решаемой задачи это лишние действия и лишняя память.

А задача такая - сохранить в EEPROM (а потом прочитать) переменную, размер которой несколько (больше одного) байт.
EEPROM умеет работать только с байтами или последовательностью байт.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 16:31 
Аватара пользователя
Возьмите указатель на нужную переменную и приведите его к unsigned char*.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 16:45 
Пишете функцию, принимающую переменную как указатель на нужный тип. А внутри меняете тип указателя на unsigned char*:
Используется синтаксис C
void f(int* p) {
unsigned char* pp;
    pp = (unsigned char*)p;
}
int a;
f(&a);
И по каждому экземпляру такой функции на каждый желаемый тип переменных.

Или сразу так:
Используется синтаксис C
void f(unsigned char* p) { ... }
f((unsigned char*)&a);
Правда тут функция должна знать сколько байт по переданному указателю. И писанины больше.

Если длину передавать вторым параметром, то можно нашлёпать таких определений:
Используется синтаксис C
void f(unsigned char* p, char n) { ... }
#define f_int(x)   f((unsigned char*)x, 4)
#define f_word(x)  f((unsigned char*)x, 2)
#define f_byte(x)  f((unsigned char*)x, 1)

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 17:27 
Я недавно изучал пресловутый формат IEEE754, так был тот же вопрос.
Копирую прямо сюда программку, которая на выходе выдает байты плавающего
числа 0.2.



Код:
#include <stdio.h>

typedef unsigned char Byte;
struct bytes {Byte b1; Byte b2; Byte b3; Byte b4;};
typedef struct bytes Bytes4;

void printbytes(Bytes4);


void printbytes(Bytes4 bytes)
{
  unsigned int n1,n2,n3,n4;

  n1=bytes.b1, n2=bytes.b2, n3=bytes.b3, n4=bytes.b4;
  printf("%u, %u, %u, %u\n", n1,n2,n3,n4);

  return;
}


main()
{
   union {float fl; Bytes4 bytes;} fb;

   fb.fl=0.2;
   printbytes(fb.bytes);

   return;
}

(конечно, как Вы поняли, я не программист ни разу...)

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 18:13 
Аватара пользователя
mihaild
Dmitriy40
vpb

Спасибо!

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 18:13 
EUgeneUS в сообщении #1360470 писал(а):
А задача такая - сохранить в EEPROM (а потом прочитать) переменную, размер которой несколько (больше одного) байт. EEPROM умеет работать только с байтами или последовательностью байт.
Ваша функция записи в EEPROM скорее всего будет уметь писать в EEPROM побайтово некоторый буфер в памяти программы некоторой длины. Прототипы функций какие-нибудь такие:

Код:
void EEPROM_write( int startAddr, const void* buff, size_t length );
void EEPROM_read( int startAddr, void* buff, size_t length );


Внутри вы кастите указалель на void к указателю на массив uint8_t (или к типу, с которым работает функция записи/чтения одного байта) и пишете/считываете буфер побайтово в цикле.

Вызов этих функций в вашей программе будет примерно таким:

Код:
double myDirtyVar;
EEPROM_write( EEPROM_DIRTY_VAR_ADDR, &myDirtyVar, sizeof(myDirtyVar) );
EEPROM_read( EEPROM_DIRTY_VAR_ADDR, &myDirtyVar, sizeof(myDirtyVar) );


И учтите, что если в процессе записи отключится питание микропроцессора, то запишется только часть состояния вашей переменной. В качественном изделии требуется защита от подобных неприятностей.

Этими же функциями можно писать/считывать произвольные структуры целиком, но требуется аккуратность с выравниванием полей структур, чтобы не расходовать зря ячейки EEPROM. Гуглите
Код:
#pragma pack

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 19:53 
Насчёт void указателя
Используется синтаксис C
f(void* ptr, size_t n) {...}
совет не всегда правильный: тут полностью отключается проверка типа передаваемого указателя, что программисту конечно удобнее (не надо помнить к какому типу преобразовывать), но допускает некоторые ошибки и опечатки, типа вызовов
Используется синтаксис C
f(&MyInt, 44);//Опечатка
#define f_int(x) f(&x, 4)
f_int(MyStr);//Опечатка или ошибка
Иногда это наоборот помогает, но столь редко, что защита от опечаток нужнее, а изредка обходной путь совсем не проблема.

Про union совет тоже хорош, но меня раздражает бессмысленное удлинение имён (да ещё и с обязательной точкой, ещё и не одной), ну и не уверен всегда ли будет достаточно эффективный код (точнее уверен что часто нет). Первое частично решается кучей #define, но требует аккуратного продумывания системы наименований. Потому не стал упоминать.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 19:58 
Аватара пользователя
realeugene
Спасибо!

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:03 
Dmitriy40 в сообщении #1360516 писал(а):
совет не всегда правильный: тут полностью отключается проверка типа передаваемого указателя, что программисту конечно удобнее (не надо помнить к какому типу преобразовывать), но допускает некоторые ошибки и опечатки, типа вызовов
Этот совет всегда правильный, когда дело касается нетипизированных буферов в памяти. В сях это специальный тип как раз для таких случаев со специальными правилами неявного кастинга. Не нужно изобретать велосипед.

А такие макросы как вы написали в примере, разумеется, писать не нужно, если вы не хотите основательно запутать читателя вашего кода. И кодировать непосредственно уже при вызове функции длину явным числом вместо использования sizeof или вместо явного вычисления длины массива, если это массив - это, тоже, пример ужасного стиля.

Если хочется завести дополнительные типизированные функции - переходники, нужно заводить типизированные функции, а не макросы. inline функции есть уже давно даже в сях, не говоря про плюсы, в которых они были изначально.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:12 
vpb в сообщении #1360488 писал(а):
Копирую прямо сюда программку, которая на выходе выдает байты плавающего
числа 0.2.

Чё то больно сложно: typedef, struct, union...
У меня вот такой код есть, не понимаю как, но он работает:

Код:
    float xf = 0.2;

    // эта строчка собсна и делает всю работу
    // т.е. помещает байты флоата в массив
    uchar *xb = (uchar *) &xf;

    // эта вывод результата на экран
    QString str = (QString::number(xb[0], 16) + "  " +
                   QString::number(xb[1], 16) + "  " +
                   QString::number(xb[2], 16) + "  " +
                   QString::number(xb[3], 16));

    ui->label_2->setText(str);


Результат: cd cc 4c 3e

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:15 
FomaNeverov в сообщении #1360522 писал(а):
У меня вот такой код есть

Это плюсы, а не plain C.
И это оффтопик. Создайте свою тему, сформулируйте корректно вопрос.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:27 
Извините, хочу уточнить, вот этот код плюсы?

Код:
    float xf = 0.2;
    uchar *xb = (uchar *) &xf;

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:30 
Я повторю, void* допускает слишком широкие неявные преобразования, что практически аннулирует для него контроль типов и соответственно опечаток и ошибок. Да, механизм стандартный, но отсутствие контроля мне не нравится.

Лично мне кстати быстро надоедает писать sizeof() в десятках и сотнях точках вызовов функций, проверяя не опечатался ли в имени переменной, и делаю нечто типа
Используется синтаксис C
#define ewr(a,x) eeprom_write(a, &x, sizeof(x))


FomaNeverov в сообщении #1360522 писал(а):
т.е. помещает байты флоата в массив
Ничего никуда она не помещает, лишь создаёт новый указатель на char, равный указателю на исходный float. Ну а адресация дальше по этому указателю как к массиву по индексу работает, но мне не нравится - отсутствием контроля со стороны компилятора.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:34 
Аватара пользователя
FomaNeverov в сообщении #1360525 писал(а):
вот этот код плюсы?
Видимо, имелось в виду, что в плюсах есть православный reinterpret_cast, который и нужно использовать для решения задачи.

 
 
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:34 
FomaNeverov в сообщении #1360525 писал(а):
Извините, хочу уточнить, вот этот код плюсы?
Этот - ещё нет. Изучайте обычное распределение памяти под переменные и преобразования типов указателей. Которое, вообще говоря, работать не обязано на всех архитектурах, но обычно работает. Преобразование переменной к массиву байт, вообще говоря, непереносимая низкоуровневая конструкция.

 
 
 [ Сообщений: 20 ]  На страницу 1, 2  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group