Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

конкретный пример я пока не могу привести, но теорию могу рассказать.

допустим, в ОЗУ по адресу 0x0100 находится некий регистр REG, младшие 4 бита которого соответствуют какому-то предделителю, а старшие 4 - это какие-то флаги.

определяем тип для обращения к этим битам:

Код: Выделить всё

typedef struct{
   unsigned char DIV : 4; // младшие 4 бита - делитель
   unsigned char FLAG1 : 1;
   unsigned char FLAG2 : 1;
   unsigned char FLAG3 : 1;
   unsigned char FLAG4 : 1;
} reg_bits;
это тип-структура с битовыми полями

но иногда нам будет надо работать с этим регистром сразу, как с байтом, поэтому создаем еще один тип-объединение:

Код: Выделить всё

typedef union{
   reg_bits BITS; // это для доступа к битам
   unsigned char BYTE; // это для доступа сразу к байту
} reg_type;


теперь остается только "отмапить" этот тип на нужный адрес в ОЗУ, и все:

Код: Выделить всё

reg_type *REG = (void*)0x0100;
так мы объявляем указатель, сразу инициализированный нужным значением (адресом). если наш регистр содержит некоторые биты/флаги, которые могут менять свое значение по каким-то аппаратным событиям, надо обязательно указывать volatile для соответствующих полей наших ранее созданных структур. ну или для указателя целиком. если содержимое этого регистра зависит только от наших действий, volatile можно не указывать

теперь при необходимости мы можем записать в регистр число сразу

Код: Выделить всё

REG->BYTE = 0x00;
или установить только предделитель

Код: Выделить всё

REG->BITS.DIV = 5;
или работать с любым из флагов

Код: Выделить всё

char data = REG->BITS.FLAG2 ? 12 : 32;


в общем, как-то так... на сколько я в курсе, подобным образом вся периферия ARM описана в соответствующих хедерах, и вся работа с нею ведется подобным образом

свежие версии компиляторов поддерживают и безымяные структуры внутри union-ов, поэтому можно чуть-чуть упростить:

Код: Выделить всё

typedef union{
   unsigned char BYTE;
   struct{
      unsigned char DIV : 4; // младшие 4 бита - делитель
      unsigned char FLAG1 : 1;
      unsigned char FLAG2 : 1;
      unsigned char FLAG3 : 1;
      unsigned char FLAG4 : 1;
   };
} reg_type;
в этом случае для обращения к битам не надо будет указывать BITS, т.е. будет так:

Код: Выделить всё

REG->DIV = 12;
data = REG->FLAG2 ? 12 : 32;


P.S. количество битовых полей в структуре может быть любым - компилятор выделит нужное количество целых байтов под структуру. но делать отдельное поле размером, например, 65 битов недопустимо, т.к. Си не имеет встроенных средств для работы с числами большими, чем long long. иначе говоря, каждое битовое поле может иметь размер в битах такой, чтобы уложиться внутри любого стандартного типа Си (кроме float).
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

Re: Вопросы по С/С++ (СИ)

Сообщение Apparatchik »

Спасибо большое, по поводу регистров понятно.

Когда битовому полю присваиваеш число, то берутся младшие байты в количестве длины поля, остальные отбрасываются или присваиваются те биты смотря где находится битовое поле в структуре?
«И всё-таки она вертится!»
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

я не понял, что вы спросили, потому отвечу, как получится :)))

когда вы задаете длину битового поля, то тем самым как бы ограничиваете длину числа в битах. не смотря на то, что битовое поле int var : 3; имеет тип int, на самом деле в нем будет всего 3 бита! честно говоря, я не в курсе, будет ли выделен один бит под знак числа или нет - как-то не доводилось сталкиваться... но смысл именно в том, что фактически число будет из 3 битов.

далее все происходит по сишным правилам: при переполнении чисел остается только то, что влезло, остальное теряется. то есть если в 3-битное поле вы попытаетесь записать число 8, то на самом деле там окажется 0 (0b00001000). положение битового поля "внутри структуры" ни на что не влияет, т.е. всегда будут браться младшие биты по нужному количеству и помещаться туда, куда следует. то есть компилятор путем сдвигов сформирует нужное число и поместит его в байт, маскируя остальные биты, чтобы не изменить их содержимое. это, безусловно, означает, что доступ к битовым полям почти неизбежно будет медленнее, чем к "обычным" полям структуры "полного размера".
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

Re: Вопросы по С/С++ (СИ)

Сообщение Apparatchik »

ARV писал(а):я не понял, что вы спросили, потому отвечу, как получится :)))

У Вас получилось, мне все понятно, Спасибо!!! Есче интересно знать, битовые поля применяются когда оперативной памяти мало или просто для удобства. На сколько это выгоднее конструкции типа var |= 1 << 4;
«И всё-таки она вертится!»
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Ассемблерный код вряд ли будет отличаться. Это, скорее, удобство для программиста.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

Apparatchik писал(а):Есче интересно знать, битовые поля применяются когда оперативной памяти мало или просто для удобства.

И для удобства тоже. Если автор, к примеру, не находит метод использования битовых масок и операций приемлемо понятным для своей аудитории.
В МК про битовые поля как правило вспоминают когда место под данные уже исчерпалось - а для кода этого места ещё осталось "шибко прельстиво обильно" - и вот, после того, как программирующий организм до отчаяния наиграется в "игру 15" целыми байтами - к нему в тревожный рассветный полудрём приходит Архимед и, лукаво подхихикивая в бороду, заставляет вспомнить про битовые поля, тут-же пробудиться с воплем "Эврика!" и одним прыжком переместиться к компьютеру. После чего та самая "игра в 15" продолжается уже на качественно новом уровне - за счёт того, что код пухнет от косвенных битовых операций, скрывающихся за этой филигранной красотой кода. При сём довольно быстро приходит понимание, что и от перестановки порядка таких переменных тоже можно выигрывать байтики. Нынче правда проще взять камешек поширше, нежели устраивать многочасовые экзерсисы с компилятором, заталкивая свои идейки в бюджетные прокрустовы восьмибитники. Хотя, нужно признать, что и экзерсисы оные порой обогащают весьма душеполезным опытом. Иначе как бы мне в межушное пространство, к примеру, дошло, что индекс для буфера размером кратным степени двойки можно сделать битовым полем соответствующей ширины, после чего смело его инкрементировать, не пачкая руки делением по модулю. Потомки, конечно, от такого компактного решения станут весьма агрессивно-благодарны, но ведь шикарно-же! ;)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

Re: Вопросы по С/С++ (СИ)

Сообщение Apparatchik »

Если я объявил структуру с битовыми полями, как её правильно инициализировать? Как обычную структуру, присвоить битовому полю число и компилятор возмет младшие биты?
«И всё-таки она вертится!»
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение Аlex »

Apparatchik писал(а):как её правильно инициализировать?
Каждое поле по-отдельности, тем значением, которое необходимо при инициализации. Это же очевидно, или нет ? :dont_know:
Про младшие биты - не понятно.
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

Re: Вопросы по С/С++ (СИ)

Сообщение Apparatchik »

Код: Выделить всё

typedef struct {
   uint8_t pole_1 :1;
   uint8_t pole_2 :4;
   uint8_t pole_3 :3;
} my_struct_type;


my_struct_type my_struct = {
   1,
   13,
   255
};


так будет правильно? несмотря на 255 я получу в третьем поле 7.
«И всё-таки она вертится!»
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Да, с соответствующим предупреждением компилятора:

Изображение
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение Аlex »

Apparatchik писал(а):так будет правильно? несмотря на 255 я получу в третьем поле 7.
Где логика, записывать в 3 бита, число, превышающее 7 ? :dont_know:
Darkmaster
Встал на лапы
Сообщения: 137
Зарегистрирован: Вс окт 11, 2009 09:54:59
Откуда: Пенза

Re: Вопросы по С/С++ (СИ)

Сообщение Darkmaster »

Доброго времени.Может кто то пояснить следующую запись

Код: Выделить всё

 #define  Maycount               ((unsigned char) 0x22) 

в частности что дает Unsigned char..и где подобное можно использовать ?
Интересная,однако,штука
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

Darkmaster писал(а):в частности что дает Unsigned char..

ЕМНИП если знаковость типа не указана явно - то предполагается signed. Если нужен беззнаковый - тогда и осуществляем вот такой акт насилия над компилятором, выражающийся в явном приведении. Шестнадцатиричная константа имеет тип int. Напрямую скормить её, например, функции, ожидающей параметром unsigned char, уважающий себя компилятор не позволит. Приведение-же типа позволяет без помех "прострелить себе ногу".
Darkmaster писал(а):и где подобное можно использовать ?

Там где ждут именно unsigned char. Ну ешё в восьмибитном ембединге существует поверье, что если все целочисленные объявлять байтовыми - то будет всё работать легко и приятно. По крайней мере изучение соответствуюших продуктов жизнедеятельности обитателей оной области оставляет подобное ощущение. ;)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Darkmaster
Встал на лапы
Сообщения: 137
Зарегистрирован: Вс окт 11, 2009 09:54:59
Откуда: Пенза

Re: Вопросы по С/С++ (СИ)

Сообщение Darkmaster »

Siarzhuk с этим понятно давно)
уточню..интересует именно макрос .Который я написал выше.
Ведь можно написать

Код: Выделить всё

#define Maycount     0x25 

мы же добавили ишо и '(unsigned char)'
Интересная,однако,штука
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

Darkmaster писал(а):с этим понятно давно) .... мы же добавили ишо и '(unsigned char)'

Чтобы привести константу 0х22, имеющую тип int, к типу unsigned char. Для чего - тут уж по коду смотреть надо в какие "физиологические полости" программы глубокоуважаемый автор эту подстановку пристраивает. Видать там, в этих местах, были не очень рады int-у - вот и заткнули рот компилятору, чтобы не вякал.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Darkmaster
Встал на лапы
Сообщения: 137
Зарегистрирован: Вс окт 11, 2009 09:54:59
Откуда: Пенза

Re: Вопросы по С/С++ (СИ)

Сообщение Darkmaster »

Siarzhuk пасибки.примерно так я думал ,что ничего интересного это не несет
строка кстати из кода под STM8..
Странно конечно
Интересная,однако,штука
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Вроде уже давно на C пишу, но вчера попался на достаточно забавный "крючок" с приведением типов.

Реализовывал NEC протокол пульта ДУ, там передаются 32 бита данных (16 бит адрес, команда и команда инверсно). Получился такой вод код (исходники структуры и проверки принятого на валидность):

Код: Выделить всё

...
typedef union {
   uint32_t raw;
   struct {
      uint8_t laddr;
      uint8_t haddr;
      uint8_t cmd;
      uint8_t ncmd;
   };
} NECCmd;
...
...
if ((uint8_t)(~necCmd.ncmd) == necCmd.cmd) {
  // команда валидна, можно обрабатывать
}
...
Тонкость заключалась в том, что без приведения типов в условии if оно не отрабатывало. То есть, несмотря на то, что вроде как и cmd, и ncmd - оба uint8_t, но результат инверсии битов получался не 8-битный. То есть, если объяснить более простым кодом, то

Код: Выделить всё

uint8_t a = 0b11001010;
uint8_t b = 0b00110101;
условие

Код: Выделить всё

if (~a == b) {
  ...
}

Не отработает, и будет вообще при оптимизиации выброшено, ибо фактически компилятор будет делать следующее

Код: Выделить всё

if (0b11111111 00110101 == 00110101) {
  ...
}
То есть результат побитовой инверсии 8-бит числа вовсе даже не 8-битен.

Вот такое вот неочевидное поведение компилятора, в которое никак не мог въехать.

P. S. "Большой" компилятор выдаёт предупреждение (решил сейчас проверить, уже после написания сообщения), avr-gcc же молчал как партизан:

Изображение

С знаковыми int8_t такого предупреждения нет и всё отрабатывает, как ожидалось.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

WiseLord писал(а):фактически компилятор будет делать следующее

Код: Выделить всё

if (0b11111111 00110101 == 00110101) {
  ...
}
То есть результат побитовой инверсии 8-бит числа вовсе даже не 8-битен.


Подозреваю, что справа там тоже не 00110101 а 00000000 00110101 - т.е. полновесный int как результат implicit conversion в контексте if. PS: И как по мне 0xFF & результата инверсии менее парадоксально выглядит в глазах потомков чем приведение. :)

WiseLord писал(а):Вот такое вот неочевидное поведение компилятора, в которое никак не мог въехать.


О да, тоже люблю такие случаи - как повод залезть в писания святых отцов и приобшиться к ещё одной крупице тайного знания, которое в беглом прочтении может быть понято лишь [практическими] шишками на определённых местах лобной стенки черепной коробки. PS: Заодно и повод разобраться с integer promotions - если ещё не. ;)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Что самое забавное, так то, что будь это x86 - 32-разрядной архитектуре, то ладно. Но ведь это на 8-битном AVR происходило, вот в чём прикол.
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

Re: Вопросы по С/С++ (СИ)

Сообщение Apparatchik »

Я, для простоты, плюсую прямую и инверсную часть команды если получаю 0xFF значит все верно можно обрабатывать. Пока работает без збоев.
«И всё-таки она вертится!»
Ответить

Вернуться в «Разные вопросы по МК»