Я видел, как этот шаблон часто используется в C и C ++.
unsigned int flags = -1; // all bits are true
Это хороший портативный способ сделать это? Или пользуется 0xffffffff
или ~0
лучше?
c++
c
binary
bit-fields
hyperlogic
источник
источник
-1
что всегда будет работать, тот факт, что после него нужен комментарий, показывает, что это нечеткий код. Если переменная должна быть набором флагов, зачем присваивать ей целое число? Его тип может быть целым числом, но определенно семантически не целым. Вы никогда не будете увеличивать или умножать его. Поэтому я бы использовал0xffffffff
не для мобильности или правильности, но для ясности.-1
остается переносимым и обратно совместимым решением для обоих языков, но это может повлиять на некоторые рассуждения в других ответах.Ответы:
Я рекомендую вам делать это именно так, как вы показали, так как это наиболее простой способ. Инициализировать,
-1
который будет работать всегда , независимо от фактического представления знака, но~
иногда будет иметь удивительное поведение, потому что вам потребуется правильный тип операнда. Только тогда вы получите самое высокое значениеunsigned
типа.В качестве примера возможного сюрприза рассмотрим этот:
Он не обязательно сохранит шаблон со всеми битами 1
a
. Но сначала он создаст шаблон со всеми битами 1 в элементеunsigned int
, а затем назначит егоa
. Что происходит, когдаunsigned long
битов больше, так это то, что не все из них равны 1.И рассмотрим этот вариант, который не сработает на представлении дополнения, отличном от двух:
Причина в том, что
~0
нужно инвертировать все биты. Инвертирование, которое даст результат-1
на машине с дополнением до двух (что нам и нужно!), Но не даст результата-1
на другом представлении. На машине с дополнением до единицы это дает ноль. Таким образом, на машине с дополнением до единицы указанное выше будет инициализированоa
нулем.Вы должны понимать, что все дело в значениях, а не в битах. Переменная инициализируется значением . Если в инициализаторе вы измените биты переменной, используемой для инициализации, значение будет сгенерировано в соответствии с этими битами. Значение, необходимое для инициализации
a
максимально возможным значением, - это-1
илиUINT_MAX
. Второй будет зависеть от типаa
- вам нужно будет использоватьULONG_MAX
дляunsigned long
. Однако первый не будет зависеть от его типа, и это хороший способ получить наибольшее значение.Мы не говорим о том
-1
, все ли биты равны единице (не всегда). И мы не говорим о том~0
, все ли биты едины (конечно, есть).Но мы говорим о том, каков результат инициализированной
flags
переменной. И для этого, только-1
будет работать с каждым типом и машиной.источник
numeric_limits<size_t>::max()
немного многословна, но и актерский состав тоже ...-1
представлены, и не спрашивает, какие биты~0
есть. Нас могут не волновать значения, но компилятор заботится. Мы не можем игнорировать тот факт, что операции работают со значениями и по ним. Значение из~0
не может быть-1
, но это значение вам нужно. См. Мой ответ и резюме @Dingo.unsigned int flags = -1;
портативный.unsigned int flags = ~0;
не переносится, потому что он полагается на представление с дополнением до двух.unsigned int flags = 0xffffffff;
не переносится, потому что предполагает 32-битные целые числа.Если вы хотите установить все биты способом, гарантированным стандартом C, используйте первый.
источник
~0
даетint
значение со всеми установленными битами. Но присвоениеint
anunsigned int
не обязательно приводит к тому, что unsigned int имеет тот же битовый шаблон, что и знаковый битовый шаблон. Это всегда имеет место только в случае представления с дополнением до 2. В представлении с дополнением до единиц или знаковой величины присвоение отрицательногоint
значенияunsigned int
результату в другом битовом шаблоне. Это связано с тем, что стандарт C ++ определяет преобразование со знаком -> без знака как значение, равное по модулю, а не значение с теми же битами.Честно говоря, я думаю, что все fff более читабельны. Что касается комментария о том, что это антипаттерн, если вам действительно важно, чтобы все биты были установлены / очищены, я бы сказал, что вы, вероятно, находитесь в ситуации, когда вы все равно заботитесь о размере переменной, что потребовало бы чего-то вроде повышения :: uint16_t и т. д.
источник
Способ, позволяющий избежать упомянутых проблем, - это просто сделать:
Портативный и в точку.
источник
flags
какconst
.unsigned int const flags = ~0u;
~0
представляет собой целое число , которое имеет все биты , установленные в 1, но когда вы затем присвоить , чтоint
кunsigned
переменнойflags
, необходимо выполнить преобразование значения из-2**31
(предполагая , что 32-битint
) , чтобы(-2**31 % 2**32) == 2**31
, который является целым числом со всеми битами, кроме первого, установлено 1.u
суффикс в вашем ответе. Это, конечно, сработает, но по-прежнему существует проблема с указанием типа данных, который вы используете (unsigned
и не больше) дважды, что может привести к ошибкам. Однако ошибка, скорее всего, появится, если назначение и объявление начальной переменной находятся дальше друг от друга.Я не уверен, что использование unsigned int для флагов является хорошей идеей в первую очередь для C ++. А как насчет битсета и тому подобного?
std::numeric_limit<unsigned int>::max()
лучше, потому что0xffffffff
предполагает, что unsigned int является 32-битным целым числом.источник
auto
.auto const flags = std::numeric_limit<unsigned>::max()
,Портативный? Да .
Хорошо? Спорный , о чем свидетельствует вся путаница, показанная в этой ветке. Достаточная ясность, чтобы ваши товарищи-программисты могли понять код без путаницы, должна быть одним из критериев, которые мы измеряем для хорошего кода.
Кроме того, этот метод подвержен предупреждениям компилятора . Чтобы обойти предупреждение без ущерба для вашего компилятора, вам потребуется явное приведение. Например,
Явное приведение требует, чтобы вы обращали внимание на целевой тип. Если вы обращаете внимание на тип цели, то, естественно, вы избежите ошибок других подходов.
Я бы посоветовал обратить внимание на целевой тип и убедиться, что нет неявных преобразований. Например:
Все это правильно и более очевидно для ваших коллег-программистов.
И с C ++ 11 : мы можем использовать,
auto
чтобы сделать любое из этих действий еще проще:Я считаю правильное и очевидное лучше, чем просто правильное.
источник
Стандарт гарантирует преобразование -1 в любой беззнаковый тип как все-единицы. Использование,
~0U
как правило, плохо, поскольку0
имеет типunsigned int
и не будет заполнять все биты большего беззнакового типа, если вы явно не напишете что-то вроде~0ULL
. В нормальных системах он~0
должен быть идентичен-1
, но поскольку стандарт допускает представления с дополнением до единицы и знаком / величиной, строго говоря, он не переносится.Конечно, всегда нормально писать,
0xffffffff
если вы знаете, что вам нужно ровно 32 бита, но -1 имеет то преимущество, что он будет работать в любом контексте, даже если вы не знаете размер типа, например макросы, которые работают с несколькими типами , или если размер типа зависит от реализации. Если вы знаете тип, другой безопасный способ , чтобы получить все-онов есть предел макросовUINT_MAX
,ULONG_MAX
,ULLONG_MAX
и т.д.Лично я всегда использую -1. Это всегда работает, и вам не нужно об этом думать.
источник
~(type)0
(ну, заполните справа,type
конечно). Приведение к нулю по-прежнему приводит к нулю, так что это ясно, а отрицание всех битов в целевом типе довольно четко определено. Однако не так часто мне действительно нужна эта операция; YMMV.neg
инструкции. Машины, которые имеют фиктивное арифметическое поведение со знаком, имеют отдельные арифметические коды операций со знаком / без знака. Конечно, действительно хороший компилятор всегда будет игнорировать подписанные коды операций даже для подписанных значений и, таким образом, получит бесплатное дополнение до двух.var = ~(0*var)
не будетvar
иметь значения, если тип без знака ужеint
. Возможноvar = ~(0U*var)
? (-1
хотя лично я все еще предпочитаю ).Пока у вас есть
#include <limits.h>
один из ваших включений, вы должны просто использоватьЕсли вам нужно много битов, вы можете использовать
Эти значения гарантированно будут иметь все биты значений результата, равные 1, независимо от того, как реализованы целые числа со знаком.
источник
Да. Как упоминалось в других ответах,
-1
это самый портативный; однако он не очень семантичен и вызывает предупреждения компилятора.Чтобы решить эти проблемы, попробуйте этот простой помощник:
Использование:
источник
ALL_BITS_TRUE ^ a
гдеa
целое число со знаком? Тип остается целым числом со знаком, а битовый шаблон (представление объекта) зависит от того, является ли цель дополнением до 2 или нет.ALL_BITS_TRUE ^ a
выдает ошибку компиляции, потому чтоALL_BITS_TRUE
неоднозначно. Однако его можно использовать какuint32_t(ALL_BITS_TRUE) ^ a
. Вы можете попробовать сами на cpp.sh :) В настоящее время я бы добавитьstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
в ,operator
чтобы быть уверенным , пользователи не пытаются использоватьint(ALL_BITS_TRUE)
. Я обновлю ответ.Я бы не стал делать -1. Это довольно неинтуитивно (по крайней мере, для меня). Присвоение подписанных данных беззнаковой переменной кажется нарушением естественного порядка вещей.
В вашей ситуации я всегда использую
0xFFFF
. (Конечно, используйте правильное количество F для переменного размера.)[Кстати, я очень редко вижу трюк с -1 в реальном коде.]
Кроме того, если вы действительно заботитесь об отдельных битов в vairable, было бы хорошей идеей , чтобы начать работу с фиксированной шириной
uint8_t
,uint16_t
,uint32_t
типы.источник
На процессорах Intel IA-32 нормально записать 0xFFFFFFFF в 64-битный регистр и получить ожидаемые результаты. Это связано с тем, что IA32e (64-битное расширение IA32) поддерживает только 32-битные немедленные. В 64-битных инструкциях 32-битные непосредственные символы расширяются до 64- битных .
Следующее является незаконным:
Следующее помещает 64 единицы в RAX:
Для полноты изложения в нижнюю часть RAX (также известного как EAX) помещается 32 единицы:
И на самом деле у меня случались сбои программ, когда я хотел записать 0xffffffff в 64-битную переменную, и вместо этого я получил 0xffffffffffffffff. В C это будет:
результат:
Я подумал опубликовать это как комментарий ко всем ответам, в которых говорилось, что 0xFFFFFFFF предполагает 32 бита, но так много людей ответили на него, я решил, что добавлю его как отдельный ответ.
источник
UINT64_C(0xffffffff)
расширяется до чего-то вроде0xffffffffuLL
, это определенно ошибка компилятора. Стандарт C в основном обсуждает значения , значение, представленное равным0xffffffff
4294967295 (а не 36893488147419103231), и никаких преобразований в целочисленные типы со знаком не видно.См. Ответ Литба для очень четкого объяснения проблем.
Я не согласен с тем, что, строго говоря, нет никаких гарантий ни в том, ни в другом случае. Я не знаю ни одной архитектуры, которая не представляла бы беззнаковое значение `` на единицу меньше двух в степени числа битов '', когда все биты установлены, но вот что на самом деле говорит Стандарт (3.9.1 / 7 плюс примечание 44):
Это оставляет возможность для одного из битов быть чем-либо вообще.
источник
Хотя
0xFFFF
(или0xFFFFFFFF
и т. Д.) Может быть легче читать, это может нарушить переносимость кода, который в противном случае был бы переносимым. Рассмотрим, например, библиотечную подпрограмму для подсчета количества элементов в структуре данных, для которых установлены определенные биты (точные биты указываются вызывающей стороной). Процедура может быть полностью независимой от того, что представляют собой биты, но все же должна иметь константу «все биты установлены». В таком случае -1 будет намного лучше, чем шестнадцатеричная константа, поскольку она будет работать с любым размером бит.Другая возможность, если
typedef
для битовой маски используется значение, - это использовать ~ (bitMaskType) 0; если битовая маска имеет только 16-битный тип, в этом выражении будет установлено только 16 бит (даже если int в противном случае будет 32 бита), но поскольку все, что требуется, будет 16 бит, все должно быть в порядке при условии, что один фактически использует соответствующий тип при приведении типов.Между прочим, у выражений формы
longvar &= ~[hex_constant]
есть неприятная ошибка, если шестнадцатеричная константа слишком велика, чтобы поместиться вint
, но она уместится вunsigned int
. Если anint
- 16 бит, тоlongvar &= ~0x4000;
илиlongvar &= ~0x10000
; очистит один битlongvar
, ноlongvar &= ~0x8000;
очистит бит 15 и все биты выше этого. Подходящие значенияint
будут иметь оператор дополнения, примененный к типуint
, но результат будет расширен знакомlong
, установив старшие биты. Для слишком больших значенийunsigned int
будет применяться оператор дополнения к типуlong
. Однако значения, находящиеся между этими размерами, будут применять оператор дополнения к типуunsigned int
, который затем будет преобразован в типlong
без расширения знака.источник
Практически: да
Теоретически: Нет.
-1 = 0xFFFFFFFF (или любой другой размер int на вашей платформе) верно только с двумя арифметическими дополнениями. На практике это будет работать, но существуют устаревшие машины (мэйнфреймы IBM и т. Д.), На которых у вас есть фактический знаковый бит, а не представление с дополнением до двух. Предлагаемое вами решение ~ 0 должно работать везде.
источник
Как уже упоминалось, -1 - это правильный способ создать целое число, которое будет преобразовано в беззнаковый тип со всеми битами, установленными в 1. Однако самая важная вещь в C ++ - это использование правильных типов. Следовательно, правильный ответ на вашу проблему (который включает ответ на заданный вами вопрос) таков:
Он всегда будет содержать точное количество битов, которые вам нужны. Он создает a
std::bitset
со всеми битами, установленными в 1 по тем же причинам, которые упоминались в других ответах.источник
Это, конечно, безопасно, так как -1 всегда будет иметь все доступные биты, но мне больше нравится ~ 0. -1 просто не имеет особого смысла для
unsigned int
.0xFF
... нехорошо, потому что это зависит от ширины шрифта.источник
Я говорю:
Это всегда даст желаемый результат.
источник
Использование того факта, что назначение всех битов одному для беззнакового типа эквивалентно взятию максимально возможного значения для данного типа
и расширению области действия вопроса на все беззнаковые целочисленные типы:
Назначение -1 работ для любого знака целого типа (без знака Int, uint8_t, uint16_t и т.д.) для обоих C и C ++.
В качестве альтернативы для C ++ вы можете:
<limits>
и используйтеstd::numeric_limits< your_type >::max()
Целью может быть добавление большей ясности, так как при назначении
-1
всегда требуется пояснительный комментарий.источник
Способ сделать значение более очевидным и при этом избежать повторения типа:
источник
да, показанное представление очень правильное, как если бы мы сделали это наоборот, вам потребуется, чтобы оператор перевернул все биты, но в этом случае логика довольно проста, если мы рассмотрим размер целых чисел в машине
например, на большинстве машин целое число составляет 2 байта = 16 бит, максимальное значение, которое оно может содержать, равно 2 ^ 16-1 = 65535 2 ^ 16 = 65536
0% 65536 = 0-1% 65536 = 65535, что соответствует 1111 ............. 1, и все биты установлены в 1 (если мы рассматриваем классы остатка по модулю 65536), следовательно, это много прямо вперед.
я думаю
нет, если вы рассматриваете это понятие, оно идеально подходит для неподписанных целых, и это действительно работает
просто проверьте следующий фрагмент программы
int main () {
}
ответ для b = 4294967295, который равен -1% 2 ^ 32 для 4-байтовых целых чисел
следовательно, это совершенно верно для целых чисел без знака
в случае каких-либо неточностей сообщите пожалуйста
источник