2014 dxdy logo

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

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




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


11/12/16
5379
Вопрос, наверное, банальный (так как задача банальная), но красивого решения не знаю. :roll:

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

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

 Профиль  
                  
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 16:31 
Заслуженный участник
Аватара пользователя


16/07/14
3112
Москва
Возьмите указатель на нужную переменную и приведите его к unsigned char*.

 Профиль  
                  
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 16:45 
Заслуженный участник


20/08/14
5786
Россия, Москва
Пишете функцию, принимающую переменную как указатель на нужный тип. А внутри меняете тип указателя на 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 
Заслуженный участник


18/01/15
1330
Я недавно изучал пресловутый формат 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 


11/12/16
5379
mihaild
Dmitriy40
vpb

Спасибо!

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


27/08/16
5465
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 
Заслуженный участник


20/08/14
5786
Россия, Москва
Насчёт 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 


11/12/16
5379
realeugene
Спасибо!

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


27/08/16
5465
Dmitriy40 в сообщении #1360516 писал(а):
совет не всегда правильный: тут полностью отключается проверка типа передаваемого указателя, что программисту конечно удобнее (не надо помнить к какому типу преобразовывать), но допускает некоторые ошибки и опечатки, типа вызовов
Этот совет всегда правильный, когда дело касается нетипизированных буферов в памяти. В сях это специальный тип как раз для таких случаев со специальными правилами неявного кастинга. Не нужно изобретать велосипед.

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

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

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


28/07/17
281
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 


27/08/16
5465
FomaNeverov в сообщении #1360522 писал(а):
У меня вот такой код есть

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

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


28/07/17
281
Извините, хочу уточнить, вот этот код плюсы?

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

 Профиль  
                  
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:30 
Заслуженный участник


20/08/14
5786
Россия, Москва
Я повторю, void* допускает слишком широкие неявные преобразования, что практически аннулирует для него контроль типов и соответственно опечаток и ошибок. Да, механизм стандартный, но отсутствие контроля мне не нравится.

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


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

 Профиль  
                  
 
 Re: Обратиться к "длинным типам", как к массиву байт (в C)
Сообщение11.12.2018, 20:34 
Заслуженный участник
Аватара пользователя


01/08/06
2604
Уфа
FomaNeverov в сообщении #1360525 писал(а):
вот этот код плюсы?
Видимо, имелось в виду, что в плюсах есть православный reinterpret_cast, который и нужно использовать для решения задачи.

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


27/08/16
5465
FomaNeverov в сообщении #1360525 писал(а):
Извините, хочу уточнить, вот этот код плюсы?
Этот - ещё нет. Изучайте обычное распределение памяти под переменные и преобразования типов указателей. Которое, вообще говоря, работать не обязано на всех архитектурах, но обычно работает. Преобразование переменной к массиву байт, вообще говоря, непереносимая низкоуровневая конструкция.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 20 ]  На страницу 1, 2  След.

Модераторы: Toucan, maxal, PAV, Karan, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group