Используйте побитовый оператор ИЛИ ( |), чтобы установить бит.
number |=1UL<< n;
Это будет nнемного number. nдолжен быть равен нулю, если вы хотите установить 1бит st и так далее n-1, если вы хотите установить nбит th.
Используйте 1ULLесли numberшире, чем unsigned long; продвижение 1UL << nне происходит до тех пор, пока после оценки того, 1UL << nгде его неопределенное поведение сместится более чем на ширину a long. То же самое относится ко всем остальным примерам.
Прояснение немного
Используйте побитовый оператор AND ( &), чтобы очистить немного.
number &=~(1UL<< n);
Это очистит nй бит number. Вы должны инвертировать битовую строку с помощью побитового оператора NOT ( ~), а затем AND it.
Немного тумблер
Оператор XOR ( ^) может быть использован для переключения немного.
number ^=1UL<< n;
Это будет переключать nй бит number.
Проверка немного
Вы не просили об этом, но я мог бы также добавить это.
Чтобы проверить немного, сдвиньте число n вправо, затем поразрядно И это:
bit =(number >> n)&1U;
Это поместит значение nth-го бита numberв переменнуюbit .
Изменение n- го бита на x
Установка для nбита th одного 1или 0может быть достигнута с помощью следующего в реализации C ++, дополненной 2:
number ^=(-x ^ number)&(1UL<< n);
Бит nбудет установлен, если xесть 1, и очищен, если xесть 0. Если xимеет какое-то другое значение, вы получите мусор. x = !!xбудет логизировать его до 0 или 1.
Чтобы сделать это независимым от поведения отрицания дополнения 2 (где -1установлены все биты, в отличие от реализации C ++ для дополнения 1 или знака / величины), используйте отрицание без знака.
number ^=(-(unsignedlong)x ^ number)&(1UL<< n);
или
unsignedlong newbit =!!x;// Also booleanize to force 0 or 1
number ^=(-newbit ^ number)&(1UL<< n);
Как правило, рекомендуется использовать типы без знака для переносимых операций с битами.
Как правило, это хорошая идея, чтобы вообще не копировать / вставлять код, и поэтому многие люди используют макросы препроцессора (например, ответ вики сообщества ниже ) или какую-то инкапсуляцию.
Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки / сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят 'myByte | = (1 << x)' в собственные инструкции установки / сброса битов, когда x равен константа, например: (1 << 5) или const без знака x = 5.
Аарон
52
бит = число & (1 << x); не будет помещать значение бита x в бит, если только бит не имеет тип _Bool (<stdbool.h>). В противном случае, бит = !! (число & (1 << x)); будет ..
Крис Янг
23
почему бы вам не сменить последний наbit = (number >> x) & 1
aaronman
42
1это intлитерал, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичного дополнения или арифметического сдвига, поэтому его лучше использовать 1U.
Сиюань Рен
50
Я предпочитаю number = number & ~(1 << n) | (x << n);менять n-й бит на x.
Jiasli
459
Использование стандартной библиотеки C ++ std::bitset<N>.
+1. Не то, чтобы std :: bitset можно было использовать из "C", но поскольку автор отметил свой вопрос как "C ++", AFAIK, ваш ответ здесь самый лучший ... std :: vector <bool> - это другой способ, если знать его плюсы и минусы
paercebal
23
@andrewdotnich: vector <bool> - это (к сожалению) специализация, которая хранит значения в виде битов. См. Gotw.ca/publications/mill09.htm для получения дополнительной информации ...
Niklas
71
Возможно, никто не упомянул это, потому что это было помечено как встроенное. В большинстве встроенных систем вы избегаете STL, как чума. И поддержка буста, вероятно, очень редкая птица среди большинства встроенных компиляторов.
Лундин
17
@ Мартин Это очень верно. Помимо определенных факторов, снижающих производительность, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает стандарты, такие как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не просто встраиваемые люди). Обычно у людей есть дела поважнее, чем проводить статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них на конкретном компиляторе.
Лундин
37
@Lundin: Ваши высказывания слишком широки (поэтому спорить бесполезно). Я уверен, что смогу найти ситуации, если они правдивы. Это не меняет мою начальную точку. Оба эти класса прекрасно подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваше первоначальное мнение о том, что STL / Boost не используется во встроенных системах, также неверно. Я уверен, что есть системы, которые их не используют, и даже системы, которые их используют, они используются разумно, но говорить, что они не используются, просто не правильно (потому что есть системы, в которых они используются).
определяет 3-битное поле (на самом деле это три 1-битных поля). Битовые операции теперь стали немного (ха-ха) проще:
Чтобы установить или очистить немного:
mybits.b =1;
mybits.c =0;
Чтобы немного переключиться:
mybits.a =!mybits.a;
mybits.b =~mybits.b;
mybits.c ^=1;/* all work */
Проверяем немного:
if(mybits.c)//if mybits.c is non zero the next line below will execute
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к методам битового поворота, описанным в предыдущих постах.
Я всегда считал использование битовых полей плохой идеей. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносным способом, кроме как по битам. Также невозможно смешать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет несколько битов одновременно. Конечно, вы можете использовать && и надеяться, что компилятор оптимизирует его корректно ...
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
34
Битовые поля плохи во многих отношениях, я мог бы почти написать книгу об этом. Фактически, мне почти пришлось сделать это для небольшой программы, которая требовала соответствия требованиям MISRA-C. MISRA-C принудительно документирует все поведение, определяемое реализацией, поэтому я написал довольно эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядковый номер, биты заполнения, байты заполнения, различные другие проблемы выравнивания, неявные и явные преобразования типов в и из битового поля, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны.
Лундин
44
Как и большинство языковых функций, битовые поля можно использовать правильно или ими можно злоупотреблять. Если вам нужно упаковать несколько небольших значений в одно целое, битовые поля могут быть очень полезны. С другой стороны, если вы начинаете делать предположения о том, как битовые поля отображаются на фактическое, содержащее int, вы просто напрашиваетесь на неприятности.
Ферруччо
4
@endolith: Это не будет хорошей идеей. Вы можете заставить его работать, но он не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора.
Ферруччо
3
@Yasky и Ferruccio, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы совместимости не только между компиляторами, но и с оборудованием. Мы иногда обманываем себя тем, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине?». Вы, ребята, встраиваемые, уважайте меня (и симпатии).
Келли С. Френч
181
Я использую макросы, определенные в заголовочном файле, для обработки установленных битов и очистки:
/* a=target variable, b=bit number to act upon 0-n */#define BIT_SET(a,b)((a)|=(1ULL<<(b)))#define BIT_CLEAR(a,b)((a)&=~(1ULL<<(b)))#define BIT_FLIP(a,b)((a)^=(1ULL<<(b)))#define BIT_CHECK(a,b)(!!((a)&(1ULL<<(b))))// '!!' to make sure this returns 0 or 1/* x=target variable, y=mask */#define BITMASK_SET(x,y)((x)|=(y))#define BITMASK_CLEAR(x,y)((x)&=(~(y)))#define BITMASK_FLIP(x,y)((x)^=(y))#define BITMASK_CHECK_ALL(x,y)(((x)&(y))==(y))// warning: evaluates y twice#define BITMASK_CHECK_ANY(x,y)((x)&(y))
Э-э, я понимаю, что это 5-летний пост, но ни в одном из этих макросов нет дублирования аргументов, Дэн
Роберт Келли,
11
BITMASK_CHECK(x,y) ((x) & (y))в ((x) & (y)) == (y)противном случае он возвращает неверный результат в многобитовой маске (например, 5против 3) / * Привет всем могильщикам :) * /
brigadir
7
1должно быть (uintmax_t)1или похоже на случай, если кто-то попытается использовать эти макросы для более longкрупного типа
ММ
2
BITMASK_CHECK_ALL(x,y)может быть реализовано как!~((~(y))|(x))
Handy999
3
@ Handy999 Немного легче понять, почему это работает после применения закона Де Моргана и реорганизации, чтобы получить!(~(x) & (y))
Тавиан Барнс
114
Иногда стоит использовать enumдля именования биты:
Поочередно вы можете сделать clearbits()функцию вместо &= ~. Почему вы используете enum для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных?
Эндолит
4
@endolith: использование enums для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что с современными компиляторами единственное преимущество по сравнению с const shortтем, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
dmckee --- котенок экс-модератора
Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Какое enum ThingFlagsзначение ThingError|ThingFlag1, например?
Луис Колорадо
6
Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют тип со знаком int. Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами. thingstate = ThingFlag1 >> 1будет, например, вызывать поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << yможет вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.
Лундин
1
@Lundin: Начиная с C ++ 11, вы можете установить базовый тип перечисления, например: enum My16Bits: unsigned short { ... };
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/typedefenum{ERROR =-1, FALSE, TRUE} LOGICAL;#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(arg,posn)((arg)^(1L<<(posn)))
Хорошо, давайте проанализируем вещи ...
Общее выражение, с которым у вас, похоже, возникают проблемы, - это "(1L << (posn))". Все это создает маску с одним битом, которая будет работать с любым целочисленным типом. Аргумент "posn" указывает позицию, в которой вы хотите бит. Если posn == 0, то это выражение будет оцениваться как:
00000000000000000000000000000001 binary.
Если posn == 8, он оценивает:
00000000000000000000000100000000 binary.
Другими словами, он просто создает поле 0 с 1 в указанной позиции. Единственная сложная часть в макросе BitClr (), где нам нужно установить один бит 0 в поле 1. Это достигается использованием дополнения 1 того же выражения, которое обозначено оператором тильда (~).
После того как маска создана, она применяется к аргументу так, как вы предлагаете, используя битовые операторы и (&), или (|), и xor (^). Поскольку маска имеет тип long, макросы будут работать так же хорошо для символов char, short, int или long.
Суть в том, что это общее решение целого класса проблем. Конечно, возможно и даже уместно переписывать эквивалент любого из этих макросов с явными значениями маски каждый раз, когда вам это нужно, но зачем это делать? Помните, подстановка макросов происходит в препроцессоре, и поэтому сгенерированный код будет отражать тот факт, что значения считаются постоянными компилятором - т.е. использовать обобщенные макросы так же эффективно, как и «изобретать колесо» каждый раз, когда вам нужно делать немного манипуляций.
Убежденный? Вот некоторый тестовый код - я использовал Watcom C с полной оптимизацией и без использования _cdecl, чтобы результирующая разборка была максимально чистой:
#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(arg,posn)((arg)^(1L<<(posn)))int bitmanip(int word){
word =BitSet(word,2);
word =BitSet(word,7);
word =BitClr(word,3);
word =BitFlp(word,9);return word;}
2 вещи об этом: (1) при просмотре ваших макросов, некоторые могут ошибочно полагать, что макросы фактически устанавливают / сбрасывают / переворачивают биты в аргументе, однако присваивания нет; (2) ваш test.c не завершен; Я подозреваю, что если бы вы запустили больше дел, вы бы обнаружили проблему (упражнение читателя)
Дан
19
-1 Это просто странное запутывание. Никогда не изобретайте язык C, скрывая синтаксис языка за макросами, это очень плохая практика. Затем некоторые странности: сначала подписывается 1L, что означает, что все битовые операции будут выполняться со знаком типа. Все, что передано в эти макросы, вернется как подписано долго. Фигово. Во-вторых, это будет очень неэффективно работать на меньших процессорах, так как оно будет работать долго, когда операции могли выполняться на уровне int. В-третьих, функционально-подобные макросы являются корнем всего зла: у вас нет никакой безопасности типов. Кроме того, предыдущий комментарий об отсутствии назначения очень действителен.
Лундин
2
Это не удастся, если argесть long long. 1Lдолжен быть максимально широкого типа, поэтому (uintmax_t)1. (Вы можете сойти с рук 1ull)
ММ
Вы оптимизировали для размера кода? На основных процессорах Intel вы получите частичный регистр при чтении AX или EAX после возврата этой функции, поскольку она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра. Haswell / Skylake не переименовывают AL отдельно, но они переименовывают AH. ).
Питер Кордес
37
Используйте побитовые операторы: &|
Чтобы установить последний бит в 000b:
foo = foo |001b
Чтобы проверить последний бит в foo:
if( foo &001b)....
Чтобы очистить последний бит в foo:
foo = foo &110b
Я использовал XXXbдля ясности. Вы, вероятно, будете работать с представлением HEX, в зависимости от структуры данных, в которую вы упаковываете биты.
Вот мой любимый битовый арифметический макрос, который работает для любого типа целочисленного массива без знака от unsigned charдо size_t(это самый большой тип, который должен быть эффективным для работы):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof*(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof*(a)))))
Это хорошо читать, но нужно знать о возможных побочных эффектах. Использование BITOP(array, bit++, |=);в цикле, скорее всего, не будет делать то, что хочет вызывающий.
Форайдт
Верно. =) Один вариант, который вы можете предпочесть, состоит в том, чтобы разделить его на 2 макроса, 1 для адресации элемента массива и другой для смещения бита на место, аля BITCELL(a,b) |= BITMASK(a,b);(оба принимают aв качестве аргумента для определения размера, но последний никогда не будет вычислять, aтак как это появляется только в sizeof).
R .. GitHub ОСТАНОВИТЬ ЛЬДА
1
@R .. Этот ответ очень старый, но в этом случае я бы предпочел функцию макросу.
ПК Luddite
Minor: 3 - й (size_t)бросок , кажется, есть только обеспечить некоторую беззнаковую математику с %. Мог (unsigned)там.
chux - Восстановить Монику
(size_t)(b)/(8*sizeof *(a))Ненужно может быть сужена bдо разделения. Только проблема с очень большими битовыми массивами. Все еще интересный макрос.
chux - Восстановить Монику
25
Поскольку это помечено как «встроенный», я предполагаю, что вы используете микроконтроллер. Все вышеперечисленные предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).
Тем не менее, во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные издержки в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микро, что дает реальную разницу, когда есть узкие петли / высокий переключатели ISR
Для тех, кто незнаком: в моем примере микро имеет общий регистр состояния выводов PORTn, который отражает выходные выводы, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-модификации-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают «1» для обозначения «пожалуйста, сделайте этот бит 1» (SET) или «пожалуйста, сделайте этот бит нулевым» (CLEAR) и «0» для «оставьте пин-код в покое». Таким образом, вы получите два адреса портов в зависимости от того, устанавливаете ли вы или очищаете бит (не всегда удобно), но гораздо более быстрая реакция и меньший собранный код.
Микро был Coldfire MCF52259, используя C в Codewarrior. Рассмотрение дизассемблера / ассемблера является полезным упражнением, поскольку оно показывает все шаги, которые ЦП должен пройти, чтобы выполнить даже самые основные операции. <br> Мы также заметили другие инструкции по переключению ЦП в циклы, критичные ко времени - ограничение переменной путем выполнения var% = max_val стоит кучу циклов ЦП каждый раз, в то время как мы выполняем, если (var> max_val) var- = max_val использует только пара инструкций. <br> Хорошее руководство по нескольким трюкам здесь: codeproject.com/Articles/6154/…
John U
Еще важнее то, что регистры ввода-вывода с отображением вспомогательной памяти обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут пойти очень плохо, если последовательность прервана.
Бен Фойгт
1
Имейте в виду, что все регистры портов будут определены как, volatileи, следовательно, компилятор не сможет выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера.
Лундин
24
Подход битового поля имеет другие преимущества во встроенной области. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
structHwRegister{unsignedint errorFlag:1;// one-bit flag fieldunsignedintMode:3;// three-bit mode fieldunsignedintStatusCode:4;// four-bit status code};structHwRegister CR3342_AReg;
Вы должны знать о порядке упаковки битов - сначала я думаю, что это MSB, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие байтовые границы.
Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.
Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся выяснить все детали относительно того, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым.
Лундин
1
@Lundin - Правда, но битовая встроенная система (особенно в аппаратных регистрах, к которой относится мой ответ) никогда не будет полезной в любом случае.
Родди
1
Возможно, не между совершенно разными процессорами. Но вы, скорее всего, хотите, чтобы он был переносимым между компиляторами и между различными проектами. И есть много встроенных «разборов», которые вообще не связаны с аппаратным обеспечением, таких как кодирование / декодирование протокола данных.
Лундин
... и если вы привыкнете использовать битовые поля во встроенном программировании, вы обнаружите, что ваш код X86 работает быстрее и эффективнее. Не в простых тестах, где у вас есть целая машина, чтобы превзойти тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - первоначальная цель которого заключалась в том, чтобы компенсировать процессоры быстрее, чем шины и медленную память.
20
Проверьте бит в произвольном месте в переменной произвольного типа:
int main(void){unsignedchar arr[8]={0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};for(int ix =0; ix <64;++ix)
printf("bit %d is %d\n", ix, bit_test(arr, ix));return0;}
Примечания:
Это разработано, чтобы быть быстрым (учитывая его гибкость) и не ветвиться. Это приводит к эффективному машинному коду SPARC при компиляции Sun Studio 8; Я также проверил это, используя MSVC ++ 2008 на amd64. Можно сделать похожие макросы для установки и очистки битов. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что оно работает для любого местоположения практически во всех типах переменных.
Если вы делаете много трюков, вы можете использовать маски, которые сделают все это быстрее. Следующие функции очень быстрые и гибкие (они позволяют переворачивать биты в битовых картах любого размера).
constunsignedcharTQuickByteMask[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,};/** Set bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTSetBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]|=TQuickByteMask[n];// Set bit.}/** Reset bit in any sized mask.
*
* @return None
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTResetBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]&=(~TQuickByteMask[n]);// Reset bit.}/** Toggle bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTToggleBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]^=TQuickByteMask[n];// Toggle bit.}/** Checks specified bit.
*
* @return 1 if bit set else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitSet(short bit,constunsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.// Test bit (logigal AND).if(bitmap[x]&TQuickByteMask[n])return1;return0;}/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitReset(short bit,constunsignedchar*bitmap){returnTIsBitSet(bit, bitmap)^1;}/** Count number of bits set in a bitmap.
*
* @return Number of bits set.
*
* @param bitmap - Pointer to bitmap.
* @param size - Bitmap size (in bits).
*
* @note Not very efficient in terms of execution speed. If you are doing
* some computationally intense stuff you may need a more complex
* implementation which would be faster (especially for big bitmaps).
* See (http://graphics.stanford.edu/~seander/bithacks.html).
*/intTCountBits(constunsignedchar*bitmap,int size){int i, count =0;for(i=0; i<size; i++)if(TIsBitSet(i, bitmap))
count++;return count;}
Обратите внимание, что для установки бита 'n' в 16-битном целом числе вы делаете следующее:
TSetBit( n,&my_int);
Вы должны убедиться, что номер бита находится в пределах диапазона битовой карты, которую вы передаете. Обратите внимание, что для процессоров с прямым порядком байтов байты, слова, слова, слова и т. Д. Правильно отображаются в памяти (основная причина, по которой процессоры с прямым порядком байтов 'лучше', чем процессоры с прямым порядком байтов, ах, я чувствую грядущую войну пламени на...).
Не используйте таблицу для функции, которая может быть реализована одним оператором. TQuickByteMask [n] эквивалентен (1 << n). Кроме того, короткая аргументация - очень плохая идея. / И% на самом деле будет делением, а не битовым сдвигом / побитовым, и потому что знаковое деление со степенью 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента unsigned int!
R .. GitHub ОСТАНОВИТЬ ЛЬДА
Какой в этом смысл? Это только делает код медленнее и сложнее для чтения? Я не вижу в этом ни одного преимущества. 1u << n легче читать для программистов на C, и, мы надеемся, может быть преобразовано в инструкцию CPU с одним тактом. С другой стороны, ваше деление будет переведено примерно в 10 тиков, или даже так плохо, как до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура справляется с делением. Что касается функции растрового изображения, то было бы более разумно иметь справочную таблицу, переводящую каждый битовый индекс в байтовый индекс, чтобы оптимизировать скорость.
Лундин
2
Что касается big / little endian, big endian будет отображать целые числа и необработанные данные (например, строки) одинаково: слева направо от msb до lsb по всему растровому изображению. Хотя little endian будет отображать целые числа слева направо как 7-0, 15-8, 23-18, 31-24, но необработанные данные все еще остаются слева направо от msb до lsb. Так что, как мало endian лучше для вашего конкретного алгоритма, полностью вне меня, кажется, наоборот.
Лундин
2
@R .. Таблица может быть полезна, если ваша платформа не может эффективно сдвигаться, как старый микрочип MCU, но, конечно, тогда деление в выборке абсолютно неэффективно
джеб
12
Использовать этот:
intToggleNthBit(unsignedchar n,int num ){if(num &(1<< n))
num &=~(1<< n);else
num |=(1<< n);return num;}
set_bit Atomicallyset a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit andreturn its old value
test_and_clear_bit Clear a bit andreturn its old value
test_and_change_bit Change a bit andreturn its old value
test_bit Determine whether a bit isset
Примечание: здесь вся операция происходит за один шаг. Таким образом, все они гарантированно являются атомарными даже на компьютерах SMP и полезны для обеспечения согласованности между процессорами.
Visual C 2010 и, возможно, многие другие компиляторы имеют прямую поддержку встроенных логических операций. Бит имеет два возможных значения, как и логическое, поэтому мы можем использовать вместо него логические - даже если они занимают больше места, чем один бит в память в этом представлении. Это работает, даже sizeof()оператор работает правильно.
Итак, на ваш вопрос IsGph[i] =1, илиIsGph[i] =0 сделайте настройку и очистку bools легко.
Чтобы найти непечатаемые символы:
// Initialize boolean array to detect UN-printable characters, // then call function to toggle required bits true, while initializing a 2nd// boolean array as the complement of the 1st.for(i=0; i<sizeof(IsGph); i++){if(IsGph[i]){IsNotGph[i]=0;}else{IsNotGph[i]=1;}}
Обратите внимание, что в этом коде нет ничего «особенного». Это немного похоже на целое число, что технически так и есть. 1-битное целое число, которое может содержать 2 значения и только 2 значения.
Однажды я использовал этот подход, чтобы найти дубликаты записей ссуды, где loan_number был ключом ISAM, используя 6-значный номер ссуды в качестве индекса в массиве битов. Слишком быстро и спустя 8 месяцев доказали, что система мэйнфреймов, с которой мы получали данные, действительно работала неправильно. Простота битовых массивов делает уверенность в их правильности очень высокой - по сравнению с поисковым подходом, например.
std :: bitset действительно реализован в виде битов большинством компиляторов
galinette
@galinette, Согласен. Заголовочный файл #include <bitset> является хорошим ресурсом в этом отношении. Кроме того, специальный класс vector <bool> для случаев, когда вам нужно изменить размер вектора. C ++ STL, 2nd Edition, Nicolai Josuttis подробно описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за головы! Я собираюсь удалить свой комментарий о судорогах. Уже достаточно мусора в сети. Я не хочу добавлять к этому.
3
Это использует по крайней мере целый байт памяти для каждого bool. Может быть, даже 4 байта для установок C89, которые используют intдля реализацииbool
ММ
@ MattMcNabb, вы правы. В C ++ размер типа int, необходимого для реализации логического значения, не указан стандартом. Я понял, что этот ответ был ошибочным некоторое время назад, но решил оставить его здесь, так как люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, наиболее полезен комментарий Galinette, как и моя библиотека битов здесь ... stackoverflow.com/a/16534995/1899861
2
@RocketRoy: Возможно, стоит изменить предложение, которое утверждает, что это пример «битовых операций».
Бен Фойгт
6
Используйте один из операторов, как определено здесь .
Чтобы установить бит, используется int x = x | 0x?;где ?позиция бита в двоичной форме.
Давайте предположим, что сначала num = 55 Integer выполняет побитовые операции (set, get, clear, toggle). n = 40 битовая позиция для выполнения побитовых операций.
Как получить немного?
Чтобы получить nthнемного правого сдвига num, nраз. Затем выполните побитовое И &с 1.
Выполните побитовое дополнение с вышеуказанным результатом. Так что n-й бит становится неустановленным, а остаток бита становится установленным, т.е.~ (1 << n) .
Наконец, выполните побитовую &операцию И с вышеуказанным результатом и num. Вышеупомянутые три шага вместе могут быть записаны как num & (~ (1 << n));
num &=(~(1<< n));// Equivalent to; num = num & (~(1 << n));
Для переключения немного мы используем побитовый ^оператор XOR . Побитовый оператор XOR оценивается в 1, если соответствующие биты обоих операндов различны, в противном случае оценивается в 0.
Что означает переключение немного, нам нужно выполнить операцию XOR с битом, который вы хотите переключить, и 1.
num ^=(1<< n);// Equivalent to; num = num ^ (1 << n);
Как это работает?
Если бит для переключения равен 0, то 0 ^ 1 => 1 .
Спасибо за подробное объяснение. Вот ссылка на практическую задачу для BIT Magic link
Чандра Шекхар
4
Как вы устанавливаете, очищаете и переключаете один бит?
Для устранения распространенной ошибки кодирования при попытке сформировать маску: 1не всегда достаточно широк
Какие проблемы случаются, когда numberэто более широкий тип, чем 1? xможет быть слишком велико для сдвига, 1 << xведущего к неопределенному поведению (UB). Даже если xон не слишком велик, он ~может не перевернуть достаточно значащих бит.
// assume 32 bit int/unsignedunsignedlonglong number = foo();unsigned x =40;
number |=(1<< x);// UB
number ^=(1<< x);// UB
number &=~(1<< x);// UB
x =10;
number &=~(1<< x);// Wrong mask, not wide enough
Чтобы застраховать 1 достаточно широко:
Код можно использовать 1ullили педантично (uintmax_t)1и позволить компилятору оптимизировать.
number |=(1ull<< x);
number |=((uintmax_t)1<< x);
Или приведение - что делает для кодирования / проверки / обслуживания вопросы поддержания правильного и актуального преобразования.
number |=(type_of_number)1<< x;
Или осторожно продвигайте 1, заставляя математическую операцию, которая является столь же широкой как тип number.
number |=(number*0+1)<< x;
Как и в большинстве битовых манипуляций, лучше всего работать с беззнаковыми типами , а не подписан теми
Интересный взгляд на старый вопрос! Ни, number |= (type_of_number)1 << x;ни number |= (number*0 + 1) << x;уместно устанавливать бит знака со знаком типа ... На самом деле, это не так number |= (1ull << x);. Есть ли портативный способ сделать это по позиции?
Chqrlie
1
@chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - это использовать неподписанные типы. Очень переносимый код со сдвигом и подписью слишком сложен, чтобы быть приемлемым.
chux - Восстановить Монику
3
Шаблонная версия C ++ 11 (помещенная в заголовок):
Этот код не работает. (Кроме того, почему у вас есть ;после определения вашей функции?)
melpomene
@ melpomene Код не сломан, я его проверил. Ты имеешь в виду, что он не скомпилируется или результат неверный? О лишних ';' Я не помню, они действительно могут быть удалены.
Йоаким Л. Кристиансен
(variable & bits == bits)?
Мельпомена
Спасибо, что заметили, это должно было быть((variable & bits) == bits)
Joakim L. Christiansen
использование std::bitsetв c ++ 11
pqnet
0
Эта программа основана на вышеуказанном решении @ Jeremy. Если кто-то хочет быстро поиграть.
Ответы:
Установка немного
Используйте побитовый оператор ИЛИ (
|
), чтобы установить бит.Это будет
n
немногоnumber
.n
должен быть равен нулю, если вы хотите установить1
бит st и так далееn-1
, если вы хотите установитьn
бит th.Используйте
1ULL
еслиnumber
шире, чемunsigned long
; продвижение1UL << n
не происходит до тех пор, пока после оценки того,1UL << n
где его неопределенное поведение сместится более чем на ширину along
. То же самое относится ко всем остальным примерам.Прояснение немного
Используйте побитовый оператор AND (
&
), чтобы очистить немного.Это очистит
n
й битnumber
. Вы должны инвертировать битовую строку с помощью побитового оператора NOT (~
), а затем AND it.Немного тумблер
Оператор XOR (
^
) может быть использован для переключения немного.Это будет переключать
n
й битnumber
.Проверка немного
Вы не просили об этом, но я мог бы также добавить это.
Чтобы проверить немного, сдвиньте число n вправо, затем поразрядно И это:
Это поместит значение
n
th-го битаnumber
в переменнуюbit
.Изменение n- го бита на x
Установка для
n
бита th одного1
или0
может быть достигнута с помощью следующего в реализации C ++, дополненной 2:Бит
n
будет установлен, еслиx
есть1
, и очищен, еслиx
есть0
. Еслиx
имеет какое-то другое значение, вы получите мусор.x = !!x
будет логизировать его до 0 или 1.Чтобы сделать это независимым от поведения отрицания дополнения 2 (где
-1
установлены все биты, в отличие от реализации C ++ для дополнения 1 или знака / величины), используйте отрицание без знака.или
Как правило, рекомендуется использовать типы без знака для переносимых операций с битами.
или
(number & ~(1UL << n))
очиститn
бит th и(x << n)
установитn
бит th вx
.Как правило, это хорошая идея, чтобы вообще не копировать / вставлять код, и поэтому многие люди используют макросы препроцессора (например, ответ вики сообщества ниже ) или какую-то инкапсуляцию.
источник
bit = (number >> x) & 1
1
этоint
литерал, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичного дополнения или арифметического сдвига, поэтому его лучше использовать1U
.number = number & ~(1 << n) | (x << n);
менять n-й бит на x.Использование стандартной библиотеки C ++
std::bitset<N>
.Или Boost версия
boost::dynamic_bitset
.Там нет необходимости катать свои собственные:
Версия Boost допускает размер набора битов времени выполнения по сравнению со стандартным набором битов размера библиотеки .
источник
Другой вариант - использовать битовые поля:
определяет 3-битное поле (на самом деле это три 1-битных поля). Битовые операции теперь стали немного (ха-ха) проще:
Чтобы установить или очистить немного:
Чтобы немного переключиться:
Проверяем немного:
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к методам битового поворота, описанным в предыдущих постах.
источник
Я использую макросы, определенные в заголовочном файле, для обработки установленных битов и очистки:
источник
BITMASK_CHECK(x,y) ((x) & (y))
в((x) & (y)) == (y)
противном случае он возвращает неверный результат в многобитовой маске (например,5
против3
) / * Привет всем могильщикам :) * /1
должно быть(uintmax_t)1
или похоже на случай, если кто-то попытается использовать эти макросы для болееlong
крупного типаBITMASK_CHECK_ALL(x,y)
может быть реализовано как!~((~(y))|(x))
!(~(x) & (y))
Иногда стоит использовать
enum
для именования биты:Затем используйте имена позже. То есть пиши
установить, очистить и проверить. Таким образом вы скрываете магические числа от остальной части вашего кода.
Кроме этого я одобряю решение Джереми.
источник
clearbits()
функцию вместо&= ~
. Почему вы используете enum для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных?enum
s для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что с современными компиляторами единственное преимущество по сравнению сconst short
тем, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.enum ThingFlags
значениеThingError|ThingFlag1
, например?int
. Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами.thingstate = ThingFlag1 >> 1
будет, например, вызывать поведение, определяемое реализацией.thingstate = (ThingFlag1 >> x) << y
может вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.enum My16Bits: unsigned short { ... };
Из snop-c.zip 's bitops.h:
Хорошо, давайте проанализируем вещи ...
Общее выражение, с которым у вас, похоже, возникают проблемы, - это "(1L << (posn))". Все это создает маску с одним битом, которая будет работать с любым целочисленным типом. Аргумент "posn" указывает позицию, в которой вы хотите бит. Если posn == 0, то это выражение будет оцениваться как:
Если posn == 8, он оценивает:
Другими словами, он просто создает поле 0 с 1 в указанной позиции. Единственная сложная часть в макросе BitClr (), где нам нужно установить один бит 0 в поле 1. Это достигается использованием дополнения 1 того же выражения, которое обозначено оператором тильда (~).
После того как маска создана, она применяется к аргументу так, как вы предлагаете, используя битовые операторы и (&), или (|), и xor (^). Поскольку маска имеет тип long, макросы будут работать так же хорошо для символов char, short, int или long.
Суть в том, что это общее решение целого класса проблем. Конечно, возможно и даже уместно переписывать эквивалент любого из этих макросов с явными значениями маски каждый раз, когда вам это нужно, но зачем это делать? Помните, подстановка макросов происходит в препроцессоре, и поэтому сгенерированный код будет отражать тот факт, что значения считаются постоянными компилятором - т.е. использовать обобщенные макросы так же эффективно, как и «изобретать колесо» каждый раз, когда вам нужно делать немного манипуляций.
Убежденный? Вот некоторый тестовый код - я использовал Watcom C с полной оптимизацией и без использования _cdecl, чтобы результирующая разборка была максимально чистой:
---- [TEST.C] ----------------------------------------- -----------------------
---- [TEST.OUT (разобранный)] -------------------------------------- ---------
---- [finis] ------------------------------------------- ----------------------
источник
arg
естьlong long
.1L
должен быть максимально широкого типа, поэтому(uintmax_t)1
. (Вы можете сойти с рук1ull
)Используйте побитовые операторы:
&
|
Чтобы установить последний бит в
000b
:Чтобы проверить последний бит в
foo
:Чтобы очистить последний бит в
foo
:Я использовал
XXXb
для ясности. Вы, вероятно, будете работать с представлением HEX, в зависимости от структуры данных, в которую вы упаковываете биты.источник
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Для новичка я хотел бы объяснить немного больше с примером:
Пример:
Используется
&
оператор проверки бита:Переключить или перевернуть:
|
оператор: установить битисточник
Вот мой любимый битовый арифметический макрос, который работает для любого типа целочисленного массива без знака от
unsigned char
доsize_t
(это самый большой тип, который должен быть эффективным для работы):Чтобы установить немного:
Чтобы немного очистить:
Чтобы немного переключиться:
Чтобы немного проверить:
и т.п.
источник
BITOP(array, bit++, |=);
в цикле, скорее всего, не будет делать то, что хочет вызывающий.BITCELL(a,b) |= BITMASK(a,b);
(оба принимаютa
в качестве аргумента для определения размера, но последний никогда не будет вычислять,a
так как это появляется только вsizeof
).(size_t)
бросок , кажется, есть только обеспечить некоторую беззнаковую математику с%
. Мог(unsigned)
там.(size_t)(b)/(8*sizeof *(a))
Ненужно может быть суженаb
до разделения. Только проблема с очень большими битовыми массивами. Все еще интересный макрос.Поскольку это помечено как «встроенный», я предполагаю, что вы используете микроконтроллер. Все вышеперечисленные предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).
Тем не менее, во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные издержки в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микро, что дает реальную разницу, когда есть узкие петли / высокий переключатели ISR
Для тех, кто незнаком: в моем примере микро имеет общий регистр состояния выводов PORTn, который отражает выходные выводы, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-модификации-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают «1» для обозначения «пожалуйста, сделайте этот бит 1» (SET) или «пожалуйста, сделайте этот бит нулевым» (CLEAR) и «0» для «оставьте пин-код в покое». Таким образом, вы получите два адреса портов в зависимости от того, устанавливаете ли вы или очищаете бит (не всегда удобно), но гораздо более быстрая реакция и меньший собранный код.
источник
volatile
и, следовательно, компилятор не сможет выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера.Подход битового поля имеет другие преимущества во встроенной области. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
Вы должны знать о порядке упаковки битов - сначала я думаю, что это MSB, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие байтовые границы.
Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.
источник
Проверьте бит в произвольном месте в переменной произвольного типа:
Пример использования:
Примечания: Это разработано, чтобы быть быстрым (учитывая его гибкость) и не ветвиться. Это приводит к эффективному машинному коду SPARC при компиляции Sun Studio 8; Я также проверил это, используя MSVC ++ 2008 на amd64. Можно сделать похожие макросы для установки и очистки битов. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что оно работает для любого местоположения практически во всех типах переменных.
источник
В целом, для растровых изображений произвольного размера:
источник
CHAR_BIT
уже определеноlimits.h
, вам не нужно вставлять свои собственныеBITS
(и фактически вы делаете свой код хуже, делая это)Эта программа должна изменить любой бит данных с 0 на 1 или с 1 на 0:
источник
Если вы делаете много трюков, вы можете использовать маски, которые сделают все это быстрее. Следующие функции очень быстрые и гибкие (они позволяют переворачивать биты в битовых картах любого размера).
Обратите внимание, что для установки бита 'n' в 16-битном целом числе вы делаете следующее:
Вы должны убедиться, что номер бита находится в пределах диапазона битовой карты, которую вы передаете. Обратите внимание, что для процессоров с прямым порядком байтов байты, слова, слова, слова и т. Д. Правильно отображаются в памяти (основная причина, по которой процессоры с прямым порядком байтов 'лучше', чем процессоры с прямым порядком байтов, ах, я чувствую грядущую войну пламени на...).
источник
Использовать этот:
источник
Расширяя
bitset
ответ:источник
Если вы хотите выполнить всю эту операцию с программированием на C в ядре Linux тогда я предлагаю использовать стандартные API ядра Linux.
См. Https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html.
Примечание: здесь вся операция происходит за один шаг. Таким образом, все они гарантированно являются атомарными даже на компьютерах SMP и полезны для обеспечения согласованности между процессорами.
источник
Visual C 2010 и, возможно, многие другие компиляторы имеют прямую поддержку встроенных логических операций. Бит имеет два возможных значения, как и логическое, поэтому мы можем использовать вместо него логические - даже если они занимают больше места, чем один бит в память в этом представлении. Это работает, даже
sizeof()
оператор работает правильно.Итак, на ваш вопрос
IsGph[i] =1
, илиIsGph[i] =0
сделайте настройку и очистку bools легко.Чтобы найти непечатаемые символы:
Обратите внимание, что в этом коде нет ничего «особенного». Это немного похоже на целое число, что технически так и есть. 1-битное целое число, которое может содержать 2 значения и только 2 значения.
Однажды я использовал этот подход, чтобы найти дубликаты записей ссуды, где loan_number был ключом ISAM, используя 6-значный номер ссуды в качестве индекса в массиве битов. Слишком быстро и спустя 8 месяцев доказали, что система мэйнфреймов, с которой мы получали данные, действительно работала неправильно. Простота битовых массивов делает уверенность в их правильности очень высокой - по сравнению с поисковым подходом, например.
источник
bool
. Может быть, даже 4 байта для установок C89, которые используютint
для реализацииbool
Используйте один из операторов, как определено здесь .
Чтобы установить бит, используется
int x = x | 0x?;
где?
позиция бита в двоичной форме.источник
0x
это префикс для литерала в шестнадцатеричном, а не двоичном.Вот некоторые макросы, которые я использую:
источник
Переменная используется
value - Data
pos - позиция бита, который нам нужно установить, очистить или переключить.
Установить немного:
Очистить немного:
Переключить немного:
источник
источник
check_nth_bit
можноbool
.Как получить немного?
nth
немного правого сдвигаnum
,n
раз. Затем выполните побитовое И&
с 1.Как это работает?
Как установить немного?
n
раз. Затем выполните побитовую|
операцию ИЛИ сnum
.Как это работает?
Как очистить немного?
n
то есть раз1 << n
.~ (1 << n)
.&
операцию И с вышеуказанным результатом иnum
. Вышеупомянутые три шага вместе могут быть записаны какnum & (~ (1 << n))
;Как это работает?
Как немного переключиться?
Для переключения немного мы используем побитовый
^
оператор XOR . Побитовый оператор XOR оценивается в 1, если соответствующие биты обоих операндов различны, в противном случае оценивается в 0.Что означает переключение немного, нам нужно выполнить операцию XOR с битом, который вы хотите переключить, и 1.
Как это работает?
0 ^ 1 => 1
.1 ^ 1 => 0
.Рекомендуемое чтение - Битовые упражнения оператора
источник
Для устранения распространенной ошибки кодирования при попытке сформировать маску:
1
не всегда достаточно широкКакие проблемы случаются, когда
number
это более широкий тип, чем1
?x
может быть слишком велико для сдвига,1 << x
ведущего к неопределенному поведению (UB). Даже еслиx
он не слишком велик, он~
может не перевернуть достаточно значащих бит.Чтобы застраховать 1 достаточно широко:
Код можно использовать
1ull
или педантично(uintmax_t)1
и позволить компилятору оптимизировать.Или приведение - что делает для кодирования / проверки / обслуживания вопросы поддержания правильного и актуального преобразования.
Или осторожно продвигайте
1
, заставляя математическую операцию, которая является столь же широкой как типnumber
.Как и в большинстве битовых манипуляций, лучше всего работать с беззнаковыми типами , а не подписан теми
источник
number |= (type_of_number)1 << x;
ниnumber |= (number*0 + 1) << x;
уместно устанавливать бит знака со знаком типа ... На самом деле, это не такnumber |= (1ull << x);
. Есть ли портативный способ сделать это по позиции?Шаблонная версия C ++ 11 (помещенная в заголовок):
источник
;
после определения вашей функции?)(variable & bits == bits)
?((variable & bits) == bits)
std::bitset
в c ++ 11Эта программа основана на вышеуказанном решении @ Jeremy. Если кто-то хочет быстро поиграть.
источник
Попробуйте одну из этих функций на языке C, чтобы изменить n бит:
Или
Или
источник
value << n
может вызвать неопределенное поведение