Пусть у нас имеется некоторый union вида Код (Text): union flags{ long all; struct{ long flag0:1; long flag1:1; ... long flag30:1; long flag31:1; }; }; который мы хотим передавать в некоторую функцию. Вопрос собственно в том, как установить в нём интересные нам флаги при вызове функции, без объявления перед ним union'а с несколькими операторами присваивания? Или как объявить для этих флагов битовые маски независимым от порядка байт образом?
Black_mirror А как вы обычно передаёте в функции массивы? Думаю что вы сначала определяете массив, наполняете его (поэлементно) и после передаёте в функцию указатель на этот массив. Тут логика почти такая же. А коли хотите всё сразу, то юзаёте long all.
T800 Мне просто сначала показалось, что #define FLAGn (1L<<n) может дать не ту маску, но сейчас склоняюсь к выводу что маска должна получиться правильная.
У Кернигана и Ричи вроде чего-то было насчёт того, как битовые поля структуры отображаются на байты - что раньше объявлено, то становится более младшими битами.
М.б использовать сделать конструктор параметризованный у union и использовать анонимные экземпляры при вызове функций? напр, как-нибудь так: Код (Text): #include <iostream> #include <cstdio> using namespace std; union flags{ long all; struct{ long flag0:1; long flag1:1; //... long flag30:1; long flag31:1; }; flags(bool b1, bool b2):flag0(b1),flag1(b2){}; }; void somefunc(flags un1); int main() { somefunc(flags(1,0)); return 0; } void somefunc(flags un1){ printf("flag0: %s\n", (un1.flag0)?"true\n":"false\n" ); printf("flag1: %s\n", (un1.flag1)?"true\n":"false\n" ); };
CyberManiac По идее, если написано long field:n, то сначала процессор должен прочитать long в котором это поле находится, а потом наложить маску/сдвинуть, чтобы достать это поле. Или не сдвигать, а объединить со сдвинутым новым значением поля и записать обратно. Делать иначе на мой взгляд будет более накладно.
Black_mirror "long" тут просто идентификатор типа, аналогичный signed int, длина в битах всегда используется та, что после ":". Уж не знаю, зачем КиР эту хрень замутили - видимо, чтобы битовые поля могли быть signed и unsigned. Но знаковое поле из одного бита, после некоторого размышления, представляется как полный ахтунг, ибо должно принимать значения 0 и -1, а не то, что обычно думают.
CyberManiac То есть вы хотите сказать, что если в union из первого сообщения установить к примеру flag7 и flag8, а затем прочитать их через all, то нет гарантий, что они окажутся рядом?
Black_mirror То, что они окажутся рядом, гарантируется стандартом. Не гарантируется порядок их следования. Часто можно увидеть, что компилируется считывание, например, соответствующего байта, если битовое поле не пересекает байтовую границу.
l_inc Не гарантируется в том смысле, что компилятор может их как со старших, так и с младших разрядов начинать размещать? То есть объявление соответствующих масок будет зависеть от компилятора?
Black_mirror Почему? Очень даже окажутся. all накладывается на структуру. Согласно КиР первое из битовых полей (flag0) окажется самым младшим битом all. Как это будет выглядеть на big-endian, правда, не в курсе, но в мэйнстрим-программах этих мутантов можно вообще в расчёт не брать.
Black_mirror Не сталкивался с компиляторами, где ранее объявленное поле не попадает в младшие биты (даже писал кроссархитектурный код, работоспособность которого от этого зависела), но согласно стандарту да: (первое подчеркнул, т.к. тоже довольно важный момент)
l_inc Из первой фразы я делаю вывод что компилятор действительно может выделять место хоть побайтово, и на big-endian установив flag7 и flag8, а затем прочитав их как long мы можем получить 0x80010000. Похоже, что самый переносимый способ объявить битовые маски это сделать 32 константных union и использовать их. Или вообще отказаться от union в пользу обычного long.
Black_mirror Эм... Да... Верно. Я её интерпретировал немного не так, но, похоже, Вы правы. Да и на little-endian теоретически можно получить 0x00008001, если ранее объявленное битовое поле будет попадать в старшие биты ячейки (т.е. flag7 на самом деле будет младшим битом байта, flag8 старшим битом следующего байта).
Black_mirror Может, но только с учетом 2го и 3го предложений. Если все битовые поля объявлены одинакового макс.размера (например, unsigned long), то условие 2-го предложения выполняется для всех бит и соотв-но они должны идти подряд друг за другом без разрывов (по крайней мере для LE, а для BE - хз). А 3-е предложение по сути означает выравнивание для полей разного размера, и хотя по стандарту оно implementation-defined, тем не менее "нормальные" компиляторы по идее должны придерживаться неких общих правил как и с выравниванием полей обычных структур PS: В мсдн где-то есть заметка о правилах выравнивании битовых полей с примерами