На этом уроке мы рассмотрим битовые флаги и битовые маски в языке С++.
Примечание: Для некоторых этот материал может показаться немного сложным. Если вы застряли или что-то не понятно — пропустите этот урок, в будущем сможете вернуться и разобраться детально. Он не столь важен для прогресса в изучении языка C++, как другие уроки, и изложен здесь в большей мере для общего развития.
Битовые флаги
Используя целый байт для хранения значения логического типа данных, вы занимаете только 1 бит, а остальные 7 из 8 — не используются. Хотя в целом это нормально, но в особых, ресурсоёмких случаях, связанных с множеством логических значений, может быть полезно «упаковать» 8 значений типа bool в 1 байт, сэкономив при этом память и увеличив, таким образом, производительность. Эти отдельные биты и называются битовыми флагами. Поскольку доступ к этим битам напрямую отсутствует, то для операций с ними используются побитовые операторы.
Примечание: На этом уроке мы будем использовать значения из шестнадцатеричной системы счисления.
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Определяем 8 отдельных битовых флагов (они могут представлять всё, что вы захотите). // Обратите внимание, в C++11 лучше использовать "uint8_t" вместо "unsigned char" const unsigned char option1 = 0x01; // шестнадцатеричный литерал для 0000 0001 const unsigned char option2 = 0x02; // шестнадцатеричный литерал для 0000 0010 const unsigned char option3 = 0x04; // шестнадцатеричный литерал для 0000 0100 const unsigned char option4 = 0x08; // шестнадцатеричный литерал для 0000 1000 const unsigned char option5 = 0x10; // шестнадцатеричный литерал для 0001 0000 const unsigned char option6 = 0x20; // шестнадцатеричный литерал для 0010 0000 const unsigned char option7 = 0x40; // шестнадцатеричный литерал для 0100 0000 const unsigned char option8 = 0x80; // шестнадцатеричный литерал для 1000 0000 // Байтовое значения для хранения комбинаций из 8 возможных вариантов unsigned char myflags = 0; // все флаги/параметры отключены до старта |
Чтобы узнать битовое состояние, используется побитовое И:
1 |
if (myflags & option4) ... // если option4 установлено - что-нибудь делаем |
Чтобы включить биты, используется побитовое ИЛИ:
1 2 |
myflags |= option4; // включаем option4 myflags |= (option4 | option5); // включаем option4 и option5 |
Чтобы выключить биты, используется побитовое И с инвертированным литералом:
1 2 |
myflags &= ~option4; // выключаем option4 myflags &= ~(option4 | option5); // выключаем option4 и option5 |
Для переключения между состояниями бит, используется побитовое исключающее ИЛИ (XOR):
1 2 |
myflags ^= option4; // включаем или выключаем option4 myflags ^= (option4 | option5); // изменяем состояния option4 и option5 |
В качестве примера возьмем библиотеку 3D-графики OpenGL, в которой некоторые функции принимают один или несколько битовых флагов в качестве параметров:
1 |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // очищаем буфер цвета и глубины |
GL_COLOR_BUFFER_BIT
и GL_DEPTH_BUFFER_BIT
определяются следующим образом (в gl2.h):
1 2 3 |
#define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 |
Вот небольшой пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> int main() { // Определяем набор физических/эмоциональных состояний const unsigned char isHungry = 0x01; // шестнадцатеричный литерал для 0000 0001 const unsigned char isSad = 0x02; // шестнадцатеричный литерал для 0000 0010 const unsigned char isMad = 0x04; // шестнадцатеричный литерал для 0000 0100 const unsigned char isHappy = 0x08; // шестнадцатеричный литерал для 0000 1000 const unsigned char isLaughing = 0x10; // шестнадцатеричный литерал для 0001 0000 const unsigned char isAsleep = 0x20; // шестнадцатеричный литерал для 0010 0000 const unsigned char isDead = 0x40; // шестнадцатеричный литерал для 0100 0000 const unsigned char isCrying = 0x80; // шестнадцатеричный литерал для 1000 0000 unsigned char me = 0; // все флаги/параметры отключены до старта me |= isHappy | isLaughing; // Я isHappy и isLaughing me &= ~isLaughing; // Я уже не isLaughing // Запрашиваем сразу несколько состояний (мы будем использовать static_cast<bool> для конвертации результатов в значения типа bool) std::cout << "I am happy? " << static_cast<bool>(me & isHappy) << '\n'; std::cout << "I am laughing? " << static_cast<bool>(me & isLaughing) << '\n'; return 0; } |
Почему битовые флаги полезны?
Внимательные читатели заметят, что в примерах с myflags
мы фактически не экономим память. 8 логических значений займут 8 байт. Но пример, приведенный выше, использует 9 байт (8 для определения параметров и 1 для битового флага)! Так зачем же тогда нужны битовые флаги?
Они используются в двух случаях:
Cлучай №1: Если у вас много идентичных битовых флагов.
Вместо одной переменной myflags
, рассмотрим случай, когда у вас есть две переменные: myflags1
и myflags2
, каждая из которых может хранить 8 значений. Если вы определите их как два отдельных логических набора, то вам потребуется 16 логических значений и, таким образом, 16 байт. Однако с использованием битовых флагов вам потребуется только 10 байт (8 для определения параметров и 1 для каждой переменной myflags
). А вот если у вас будет 100 переменных myflags
, то, используя битовые флаги, вам потребуется 108 байт вместо 800. Чем больше идентичных переменных вам нужно, тем более значительной будет экономия памяти.
Давайте рассмотрим конкретный пример. Представьте, что вы создаете игру, в которой игроку придется бороться с монстрами. Монстр, в свою очередь, может быть устойчив к определенным типам атак (выбранных случайным образом). В игре есть следующие типы атак: яд, молнии, огонь, холод, кража, кислота, паралич и слепота.
Чтобы отследить, к какому типу атаки монстр устойчив, мы можем использовать одно логическое значение на сопротивление (для одного монстра). Это 8 логических значений (сопротивлений) для одного монстра = 8 байт.
Для 100 монстров это будет 800 переменных типа bool и 800 байт памяти.
А вот используя битовые флаги:
1 2 3 4 5 6 7 8 |
const unsigned char resistsPoison = 0x01; const unsigned char resistsLightning = 0x02; const unsigned char resistsFire = 0x04; const unsigned char resistsCold = 0x08; const unsigned char resistsTheft = 0x10; const unsigned char resistsAcid = 0x20; const unsigned char resistsParalysis = 0x40; const unsigned char resistsBlindness = 0x80; |
Нам нужен будет только 1 байт для хранения сопротивления каждого монстра и одноразовая плата в 8 байт для типов атак.
Таким образом, потребуется только 108 байт или примерно в 8 раз меньше памяти.
В большинстве программ, сохраненный объем памяти с использованием битовых флагов не стоит добавленной сложности. Но в программах, где есть десятки тысяч или даже миллионы похожих объектов, их использование может значительно сэкономить память. Согласитесь, знать о таком полезном трюке не помешает.
Случай №2: Представьте, что у вас есть функция, которая может принимать любую комбинацию из 32 различных вариантов. Одним из способов написания такой функции является использование 32 отдельных логических параметров:
1 |
void someFunction(bool option1, bool option2, bool option3, bool option4, bool option5, bool option6, bool option7, bool option8, bool option9, bool option10, bool option11, bool option12, bool option13, bool option14, bool option15, bool option16, bool option17, bool option18, bool option19, bool option20, bool option21, bool option22, bool option23, bool option24, bool option25, bool option26, bool option27, bool option28, bool option29, bool option30, bool option31, bool option32); |
Затем, если вы захотите вызвать функцию с 10-м и 32-м параметрами, установленными как true — вам придется сделать следующее:
1 |
someFunction(false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true); |
Т.е. перечислить все варианты как false, кроме 10 и 32 — они true. Читать такой код сложно, да и требуется держать в памяти порядковые номера нужных параметров (10 и 32 или 11 и 33?). Такая простыня не может быть эффективной.
А вот если определить функцию с помощью битовых флагов:
1 |
void someFunction(unsigned int options); |
То можно выбирать и передавать только нужные параметры:
1 |
someFunction(option10 | option32); |
Кроме того, что это читабельнее, это также эффективнее и производительнее, так как включает только 2 операции (1 побитовое ИЛИ и 1 передача параметров).
Вот почему в OpenGL используют битовые флаги вместо длинной последовательности логических значений.
Также, если у вас есть неиспользуемые битовые флаги и вам нужно позже добавить параметры, вы можете просто определить битовый флаг. Нет необходимости изменять прототип функции, а это плюс к обеспечению обратной совместимости.
Введение в std::bitset
Все эти биты, битовые флаги, операции-манипуляции — всё это утомительно, не правда ли? К счастью, в Стандартной библиотеке C++ есть такой объект, как std::bitset, который упрощает работу с битовыми флагами.
Для его использования необходимо подключить заголовочный файл bitset, а затем определить переменную типа std::bitset, указав необходимое количество бит. Она должна быть константой типа compile time.
1 2 3 |
#include <bitset> std::bitset<8> bits; // нам нужно 8 бит |
При желании std::bitset можно инициализировать начальным набором значений:
1 2 3 4 |
#include <bitset> std::bitset<8> bits(option1 | option2) ; // начнем с включенных option1 и option2 std::bitset<8> morebits(0x2) ; // начнем с битового шаблона 0000 0010 |
Обратите внимание, наше начальное значение конвертируется в двоичную систему. Так как мы ввели шестнадцатеричное 2, то std::bitset преобразует его в двоичное 0000 0010.
В std::bitset есть 4 основные функции:
функция test() — позволяет узнать значение бита (0 или 1).
функция set() — позволяет включить биты (если они уже включены, то ничего не произойдет).
функция reset() — позволяет выключить биты (если они уже выключены, то ничего не произойдет).
функция flip() — позволяет изменить значения бит на противоположные (с 0 на 1 или с 1 на 0).
Каждая из этих функций принимает в качестве параметров позиции бит. Позиция крайнего правого бита (последнего) равна 0, затем порядковый номер растет с каждым последующим битом влево (1, 2, 3, 4 и т.д.). Старайтесь давать содержательные имена битовым индексам (либо путем присваивания их константным переменным, либо с помощью перечислений — о них мы поговорим на соответствующем уроке).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> #include <bitset> // Обратите внимание, используя std::bitset, наши options соответствуют порядковым номерам бит, а не их значениям const int option_1 = 0; const int option_2 = 1; const int option_3 = 2; const int option_4 = 3; const int option_5 = 4; const int option_6 = 5; const int option_7 = 6; const int option_8 = 7; int main() { // Помните, что отсчет бит начинается не с 1, а с 0 std::bitset<8> bits(0x2); // нам нужно 8 бит, начнем с битового шаблона 0000 0010 bits.set(option_5); // включаем 4-й бит - его значение изменится на 1 (теперь мы имеем 0001 0010) bits.flip(option_6); // изменяем значения 5-го бита на противоположное (теперь мы имеем 0011 0010) bits.reset(option_6); // выключаем 5-й бит - его значение снова 0 (теперь мы имеем 0001 0010) std::cout << "Bit 4 has value: " << bits.test(option_5) << '\n'; std::cout << "Bit 5 has value: " << bits.test(option_6) << '\n'; std::cout << "All the bits: " << bits << '\n'; return 0; } |
Результат выполнения программы:
Bit 4 has value: 1
Bit 5 has value: 0
All the bits: 00010010
Обратите внимание, отправляя переменную bits
в std::cout — выводятся значения всех бит в std::bitset.
Помните, что инициализируемое значение std::bitset рассматривается как двоичное, в то время как функции std::bitset используют позиции бит!
std::bitset также поддерживает стандартные побитовые операторы (|
, &
и ^
), которые также можно использовать (они полезны при проведении операций одновременно сразу с несколькими битами).
Вместо выполнения всех побитовых операций вручную, рекомендуется использовать std::bitset, так как он более удобен и менее подвержен ошибкам.
Битовые маски
Включение, выключение, переключение или запрашивание сразу нескольких бит можно осуществить в одной битовой операции. Когда мы соединяем отдельные биты вместе, в целях их модификации как группы, то это называется битовой маской.
Рассмотрим пример. В следующей программе мы просим пользователя ввести число. Затем, используя битовую маску, мы сохраняем только последние 4 бита, значения которых и выводим в консоль:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int main() { const unsigned int lowMask = 0xF; // битовая маска для хранения последних 4 бит (шестнадцатеричный литерал для 0000 0000 0000 1111) std::cout << "Enter an integer: "; int num; std::cin >> num; num &= lowMask; // удаляем первые биты, оставляя последние 4 std::cout << "The 4 low bits have value: " << num << '\n'; return 0; } |
Результат выполнения программы:
Enter an integer: 151
The 4 low bits have value: 7
151
в десятичной системе равно 1001 0111
в двоичной. lowMask
— это 0000 1111
в 8-битной двоичной системе. 1001 0111 & 0000 1111 = 0000 0111
, что равно десятичному 7
.
Пример с RGBA
Цветные дисплейные устройства, такие как телевизоры и мониторы, состоят из миллионов пикселей, каждый из которых может отображать точку цвета. Точка цвета состоит из 3 пучков: один красный, один зелёный и один синий (сокр. «RGB» от англ. «Red, Green, Blue»). Изменяя их интенсивность, можно воссоздать любой цвет. Количество цветов R, G и В в одном пикселе представлено 8-битным целым числом unsigned. Например, красный цвет имеет R = 255, G = 0, B = 0; фиолетовый: R = 255, G = 0, B = 255; серый: R = 127, G = 127, B = 127.
Используется еще 4-е значение, которое называется А. «А» от англ. «Alfa», которое отвечает за прозрачность. Если А = 0, то цвет полностью прозрачный. Если А = 255, то цвет непрозрачный.
В совокупности R, G, В и А составляют одно 32-битное целое число, с 8 битами для каждого компонента:
32-битное значение RGBA | |||
31-24 бита | 23-16 бит | 15-8 бит | 7-0 бит |
RRRRRRRR | GGGGGGGG | BBBBBBBB | AAAAAAAA |
red | green | blue | alpha |
Следующая программа просит пользователя ввести 32-битное шестнадцатеричное значение, а затем извлекает 8-битные цветовые значения R, G, B и A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> int main() { const unsigned int redBits = 0xFF000000; const unsigned int greenBits = 0x00FF0000; const unsigned int blueBits = 0x0000FF00; const unsigned int alphaBits = 0x000000FF; std::cout << "Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): "; unsigned int pixel; std::cin >> std::hex >> pixel; // std::hex позволяет вводить шестнадцатеричные значения // Используем побитовое И для изоляции красных пикселей, а затем сдвигаем значение вправо в диапазон 0-255 unsigned char red = (pixel & redBits) >> 24; unsigned char green = (pixel & greenBits) >> 16; unsigned char blue = (pixel & blueBits) >> 8; unsigned char alpha = pixel & alphaBits; std::cout << "Your color contains:\n"; std::cout << static_cast<int>(red) << " of 255 red\n"; std::cout << static_cast<int>(green) << " of 255 green\n"; std::cout << static_cast<int>(blue) << " of 255 blue\n"; std::cout << static_cast<int>(alpha) << " of 255 alpha\n"; return 0; } |
Результат выполнения программы:
Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): FF7F3300
Your color contains:
255 of 255 red
127 of 255 green
51 of 255 blue
0 of 255 alpha
В программе, приведенной выше, побитовое И используется для запроса 8-битного набора, который нас интересует, затем мы его сдвигаем вправо в диапазон от 0 до 255 для хранения и вывода.
Примечание: RGBA иногда может храниться как ARGB. В таком случае, главным байтом является альфа.
Заключение
Давайте кратко повторим то, как включать, выключать, переключать и запрашивать битовые флаги.
Для запроса битового состояния используется побитовое И:
1 |
if (myflags & option4) ... // если установлен option4, то делаем что-нибудь |
Для включения бит используется побитовое ИЛИ:
1 2 |
myflags |= option4; // включаем option4 myflags |= (option4 | option5); // включаем option4 и option5 |
Для выключения бит используется побитовое И с инвертированным литералом:
1 2 |
myflags &= ~option4; // выключаем option4 myflags &= ~(option4 | option5); // выключаем option4 и option5 |
Для переключения между битовыми состояниями используется побитовое исключающее ИЛИ (XOR):
1 2 |
myflags ^= option4; // включаем или выключаем option4 myflags ^= (option4 | option5); // изменяем на противоположные option4 и option5 |
Тест
Есть следующий фрагмент кода:
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { unsigned char option_viewed = 0x01; unsigned char option_edited = 0x02; unsigned char option_favorited = 0x04; unsigned char option_shared = 0x08; unsigned char option_deleted = 0x80; unsigned char myArticleFlags; return 0; } |
Примечание: Статья — это myArticleFlags
.
Задание №1
Добавьте строку кода, чтобы пометить статью как уже прочитанную (option_viewed
).
Задание №2
Добавьте строку кода, чтобы проверить, была ли статья удалена (option_deleted
).
Задание №3
Добавьте строку кода, чтобы открепить статью от закрепленного места (option_favorited
).
Задание №4
Почему следующие две строки идентичны?
1 2 |
myflags &= ~(option4 | option5); // выключаем option4 и option5 myflags &= ~option4 & ~option5; // выключаем option4 и option5 |
Ответы
Ответ №1
myArticleFlags |= option_viewed;
Ответ №2
if (myArticleFlags & option_deleted) …
Ответ №3
myArticleFlags &= ~option_favorited;
Ответ №4
Законы Де Моргана гласят, что если мы используем побитовое НЕ, то операторы И и ИЛИ меняются местами. Следовательно, ~(option4 | option5)
становится ~option4 & ~option5
.
Байты сдвигаются вправо чтобы диапазон значений был 0-255 если этого не сделать то значение не поместится в int и выведет нуль т.к произойдёт переполнение и байты просто потеряются
В примере с RGBA не обязательно использовать битовые маски, достаточно простого сдвига ">>" , если переменная pixel нам больше не нужна. Если нужна, то можно сохранить ее значение в другой переменной.
Добрые люди, объясните пожалуйста строчки из статьи:
Зачем нужно сдвигать биты вправо на 24? То есть FF000000 в двоичной 11111111 00000000 00000000 00000000(Разделил по 8 для удобного подсчета)
Мы сдвигаем 11111111 за пределы числа? Для чего? То есть после сдвига биты теряются навсегда и получится
00000000 00000000 00000000
так ведь?
Ну ладно , даже если число останется 32 битным по-прежнему будет так:
00000000 00000000 00000000 11111111
но ведь в следующей строке это:
unsigned char green = (pixel & greenBits) >> 16;
получается:
11111111 00000000 00000000 11111111
сдвигаемся на 16
и выходит единицы в единицах?(Да звучит странно)
Объясните пожалуйста , я правда не могу понять.
"Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): FF7F3300
Your color contains:
255 of 255 red
127 of 255 green
51 of 255 blue
0 of 255 alpha"
Из ответа можно догадаться в чём смысл "сдвига вправо".
Мы видим интенсивность цветов красного(255 из 255) зелёного(127 из 255)
синего(51 из 255) и прозрачность(0 из 255).
В данном случае pixel не затирается новым значением, поэтому биты не затрутся, но на каждом шаге мы получим биты из нужной (незамаскарированной) группы, которые сдвинуты вправо — в младшие 8 бит, в диапазон от 0 до 255.
Попробовал сделать таким образом.
Благодарствую за предоставленный самоучитель.
Ради эксперимента написал код, задача которого принимать, хранить, и выводить значения сопротивлений стихиям наших баранов, то есть монстров, к холоду, огню, яду, молниям, с помощью битоых операций и флагов, по аналогии с RGBA. Берем монстра, присваиваем ему какие то резисты, конвертируем в 32битный RGBA формат, и накладывая маску выводим на экран. Как бы экономя при этом память. Я использовал всего 2 монстра, и экономии тут естественно нет, да и переменных получилось не мало. Но пойдет, как наглядный пример. Столько комментариев оставил больше для себя.
И за это время образовались очевидные вопросы. Что если к этим 4-м атрибутам потребуется добавить еще парочку? Видимо сужать диапазон отведенный под каждый атрибут, или переходить на 64бита. А если их будет еще больше? А если придет нужда дать им возможность быть отрицательными(например -300 сопротивления холоду у африканского монстра)? Стало быть такой способ хранения может станет неприемлемым? Есть ли вообще такая практика или всё приходит к отдельным переменным?
Многое зависит от самих монстров. Если все монстры уникальны и их резисты различны, то тут сложно эффективно хранить резисты. Если же у них есть разбиение по типам и значения резистов в типе одинаковы, то выгоднее хранить флаги на резисты, а сами значения хранить отдельно.
Написал класс BitFlagsArticle используя код из теста:
BitFlagsArticle.h:
BitFlagsArticle.cpp:
Source.cpp:
Технология интересная, но мне любопытно, имеет ли она смысл в концепции ооп ? Там ведь все опции для объектов одного класса можно задать в полях этого класса. Если компилятор хорошо оптимизирует память при создании объектов, то флаги не требуются, наверное.
На компилятор никогда надеяться нельзя. Вот представь: пишешь ты карточную игру и у тебя есть настройки количества игроков (2,3,4,5,6) и выбрать можно произвольное количество (хоть ни одного, хоть все). Можно, конечно, всюду bool пихнуть, но потом с таким интерфейсом крайне неприятно работать и в итоге появится ещё один слой абстракции, лишь бы упростить такую работу. Битовые флаги прекрасно решат проблему.
Интересная мысль. Да в С++ можно сделать поля в 1 бит.
Но как инициализировать несколько полей сразу?
С флагами это можно сделать просто c | f.
Со структурой придётся так:
К сожалению С++ так не умеет (или я не прав?):
С Наступившим 2020! Встал (повис:-) вопрос: значения RGBA изменяются строго бит за битом, например 0000'0000 -> 0000'0001 -> 0000'0011 -> 0000'0111 -> 0000'0011 -> и т. д… т. е. как загораются светодиоды на электронном индикаторе уровня записи? Или в произвольном порядке: 0000'0000 -> 0000'0100 -> 1000'0111 -> 0101'0110 -> и т. д.?
Код ниже превращает монитор в светофор:
файл с заголовком ToolFor.h
файл zvit.cpp с функцией main:
Уррра! Допетрил сам — код в файле zvit.cpp должен быть такой:
Что-то не понимаю в чём преимущества битового флага. Мы же ещё 8 переменных типа char определяем?
Выше было написано в чём преимущество, оно возникает в том случае, когда переменных приблизительно 100 и более, в таком случае нам понадобится 100 флагов и 8 характеристик этих флагов(включение и исключение), без использования флагов было бы 800 переменных
Можно ли вместо включения бита сразу пользоваться переключателем ^=?
Эта тема сначала показалась жутко сложной, но как только пошла практика, меня осенило.
Иногда даже удивляешься, как люди умеют использовать имеющиеся ресурсы с такой гениальной экономией.
Вот кстати практика. И конечно нужно было всё собрать в кучу 🙂
Сначала мозг ушёл в бунт после строчки
но потом всё стало интуитивно понятно. Кроме момента, когда просим пользователя ввести значения пикселя в 16-ричной системе исчисления. Я тут одна не понимаю, причём тут 16-ричная система исчисления и как пользователь (например, я) может осознанно что-то в ней вводить, предварительно не прикинув всё в десятеричной или хотя бы двоичной системе?
Перечитала 36 урок и поняла, почему 0х10 = 0001 0000 и так далее.
Суффикс 0х означает, что запись дальше — в 16-ричной системе.
а я так и не понял, почему 0х10 = 0001 0000, расскажите плз)
36 урок
Потому что одна шестнадцатеричная (дальше hex) цифра занимает 4 бита, с соответсвующим ей значением, например для цифры 5 из hex соответствует значение 0101, цифре 8 из hex — 1000. Если писать их вместе 0x58, то получится 0101 1000.
Попробовал все объединить и вот что вышло :
Здравствуйте. У меня вопросы и замечания к Вашему коду (не просто так ведь Вы его на всеобщее обозрение выложили?):
1) зачем
?
2)
насколько я понимаю, после условия if если хотим, чтобы действие выполнялось только при этом условии, точки с запятой быть не должно, иначе это два разных "стэйтмента".
И второе. Вы, видимо, предполагаете, что если есть нужная единица для option_deleted, то всё выражение не будет 0, то есть будет истинным. Это вроде и понятно, но дойти до этой логической цепочки можно не сразу. Я сделала так:
16 — это 0001 0000, у него есть одна из двух нужных единиц для deleted. Может, можно и лучше сделать. На мой взгляд тут есть сложный момент. Т.к. удалённая статья в двоичной системе должна иметь вид *1*1 ****
3) "удалИна", хоть и закомментировано и к коду не относится, но всё равно режет глаза.
Я была не права, переводя 0х80 в двоичную систему, как из десятичной с результатом 0101 0000. На самом деле это запись в шестнадцатеричной системе, а значит 1000 0000 и действительно можно написать
так как условие после if будет true только если myArticleFlags будет иметь вид 1*** ****
Здравствуйте! =) Как у вас успехи с C++ ? Вы его все ещё изучаете или уже — всё? =)
Если ещё да — можно задать несколько вопросов??
ответ Виктору: да, ещё изучаю, сейчас на 120-м уроке. Странно, что Вы хотите задать вопрос именно мне, но если так, то задавайте.
Ничего странного. Элементарный "недорандом"… ))
Вопрос тоже "странный". Не хотите ли в компании изучать ++?
Прочитал тут одну книженцию об обучении, там весьма убедительно рекомендуют делать это в паре, или не большой группе, по огромному ряду причин.
ответ Виктору: почему бы и нет? Я прерывалась на три недели, сейчас на 160 уроке. Давайте свяжемся как-то иначе и обсудим, что мы можем предпринять для повышения эффективности обучения. Моя почта amolchkova@mail.ru и скайп anastasiya.molchkova
Написал и туда, и туда, на всякий случай ещё и сюда пишу. Вот
email: cosintup@gmail.com
А не проще использовать битовые поля?
Так вроде ещё удобней, вообще не надо никаких битовых операций)
битовые поля это хорошо, но побитовые операции несут за собой много интересных преимуществ
читать нужно внимательнее
Сложная тема, понял не до конца. 🙁
да ладно, в хваленом с++ нельзя отформатировать литерал??? нули в начале не увеличивают память, они нужны как заполнитель байта.
может быть я непонятно объясняю:
х = 0х00abcdef //hex
в двоичном коде:
вывод по умолчанию (грубо 3 байта)
1010 1011 1100 1101 1110 1111
однако резерв 4 байта, куда делись нули в начале?
0 0 a b c d e f
0000 0000 1010 1011 1100 1101 1110 1111
понятно что они ничего не значат, они нужны для правильного расчёта функций
Виноват. Неправильно понял ваш вопрос. Нужно заполнить вручную те 4 пустые биты с помощью побитовых операторов.
в таком случае нужно еще проверять каждое значение функции, а это допольная операция, мне хочется найти решение чтобы значение переменной при всех раскладах было бы в формате 32 бита
вывод в hex или bin неважно, нули на своем месте
Здесь уже помочь не смогу, чтобы значение переменной при всех раскладах было бы в формате 32 бита — значит нужно всё равно заполнить полностью эти 32 бита вручную — моё видение проблемы. Стандартное решение (и есть ли оно) мне неизвестно, попробуйте спросить на Stackoverflow.
добрый день
как указать в с++ чтобы переменная unsigned long int была в любом случае 32 бита, даже если она полностью состоит из 0.
например в hex формате число 0x06ca6351 зайдёт в переменную как 6са6351
А почему вы взяли , что так вообще можно.Коль уж каждой переменой типа(int) выделяется своя память , то она или выделяется или нет , так как откуда компьютер знает , не запихнете ли вы в него число побольше 0 , например даже 21902.Он динамически в работе программы в статические переменые просто бы не выделял новой памяти.P.S мой ответ нет , нельзя.