Безопасно ли использовать -1 для установки всех битов в значение true?

StackOverflow https://stackoverflow.com/questions/809227

  •  03-07-2019
  •  | 
  •  

Вопрос

Я видел, как этот шаблон часто используется в C и C++.

unsigned int flags = -1;  // all bits are true

Это хороший портативный способ добиться этого?Или использует 0xffffffff или ~0 лучше?

Это было полезно?

Решение

Я рекомендую вам сделать это именно так, как вы показали, поскольку это наиболее простой вариант.Инициализировать до -1 который будет работать всегда, независимо от фактического представления знака, в то время как ~ иногда будет иметь неожиданное поведение, потому что вам нужно будет иметь правильный тип операнда.Только тогда вы получите максимально высокое значение unsigned тип.

В качестве примера возможного сюрприза рассмотрим следующий:

unsigned long a = ~0u;

Он не обязательно сохранит шаблон со всеми битами 1 в a.Но сначала он создаст шаблон со всеми битами 1 в unsigned int, а затем назначьте его a.Что происходит, когда unsigned long имеет больше битов, то не все из них равны 1.

И рассмотрим этот вариант, который потерпит неудачу в представлении с дополнением, отличным от двух:

unsigned int a = ~0; // Should have done ~0u !

Причина этого в том, что ~0 необходимо инвертировать все биты.Инвертирование, которое даст -1 на машине с дополнением до двух (именно это значение нам нужно!), но будет нет урожай -1 на другом представительстве.На машине с единственным дополнением это дает ноль.Таким образом, на машине с дополнением вышеизложенное будет инициализировано a до нуля.

Вы должны понимать, что все дело в значениях, а не в битах.Переменная инициализируется с помощью ценить.Если в инициализаторе вы измените биты переменной, используемой для инициализации, значение будет сгенерировано в соответствии с этими битами.Значение, которое вам нужно для инициализации a до максимально возможного значения, -1 или UINT_MAX.Второе будет зависеть от типа a - вам нужно будет использовать ULONG_MAX для unsigned long.Однако первое не будет зависеть от его типа, и это хороший способ получить максимальное значение.

Мы нет говорить о том, является ли -1 имеет все биты один (это не всегда).И мы нет говорить о том, является ли ~0 имеет все биты один (конечно, имеет).

Но мы говорим о том, какой результат инициализированного flags переменная есть.И для этого, только -1 будет работать с любым типом и машиной.

Другие советы

  • unsigned int flags = -1; является портативным.
  • unsigned int flags = ~0; не портативно, потому что он опирается на представление о двух.
  • unsigned int flags = 0xffffffff; не портативно, потому что он предполагает 32-битный INT.

Если вы хотите установить все биты способом, гарантированным стандартом C, используйте первый.

Честно говоря, я думаю, что все fff более читабельны.Что касается комментария о том, что это антишаблон, то если вас действительно волнует, чтобы все биты были установлены/очищены, я бы сказал, что вы, вероятно, находитесь в ситуации, когда вас все равно волнует размер переменной, что потребует чего-то вроде повышения ::uint16_t и т. д.

Чтобы избежать упомянутых проблем, нужно просто сделать:

unsigned int flags = 0;
flags = ~flags;

Портативно и по делу.

Я не уверен, что использование беззнакового целого числа для флагов является хорошей идеей в первую очередь в C++.А как насчет битсета и тому подобного?

std::numeric_limit<unsigned int>::max() лучше, потому что 0xffffffff предполагает, что unsigned int — это 32-битное целое число.

unsigned int flags = -1;  // all bits are true

«Это хороший портативный способ добиться этого?»

Портативный? Да.

Хороший? спорный, о чем свидетельствует вся путаница, показанная в этой теме.Быть достаточно ясным, чтобы ваши коллеги-программисты могли понять код без путаницы, должно быть одним из показателей, которые мы измеряем для хорошего кода.

Кроме того, этот метод склонен к предупреждения компилятора.Чтобы избежать предупреждения, не нанося вреда компилятору, вам понадобится явное приведение типов.Например,

unsigned int flags = static_cast<unsigned int>(-1);

Явное приведение требует, чтобы вы обратили внимание на целевой тип.Если вы обратите внимание на тип цели, вы, естественно, избежите ловушек других подходов.

Я бы посоветовал обратить внимание на целевой тип и убедиться в отсутствии неявных преобразований.Например:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Все это правильно и более очевидно вашим коллегам-программистам.

И с С++ 11:Мы можем использовать auto чтобы сделать любое из них еще проще:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Я считаю правильное и очевидное лучше, чем просто правильное.

Преобразование -1 в любой беззнаковый тип гарантировано стандартом чтобы получить все-единицы.Использование ~0U вообще плохо, так как 0 имеет тип unsigned int и не будет заполнять все биты большего беззнакового типа, если вы явно не напишете что-то вроде ~0ULL.В нормальных системах ~0 должно быть идентично -1, но поскольку стандарт допускает представления в виде дополнения до единиц и знака/величины, строго говоря, он не переносим.

Конечно, всегда можно написать 0xffffffff если вы знаете, что вам нужно ровно 32 бита, но -1 имеет то преимущество, что оно будет работать в любом контексте, даже если вы не знаете размер типа, например, макросы, которые работают с несколькими типами, или если размер типа варьируется в зависимости от реализации.Если вы знаете тип, еще один безопасный способ получить все единицы — это макросы предела. UINT_MAX, ULONG_MAX, ULLONG_MAX, и т. д.

Лично я всегда использую -1.Это всегда работает, и вам не нужно об этом думать.

Да.Как упоминалось в других ответах, -1 является самым портативным;однако это не очень семантично и вызывает предупреждения компилятора.

Чтобы решить эти проблемы, попробуйте этот простой помощник:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Использование:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

Пока у вас есть #include <limits.h> в качестве одного из ваших включений вам следует просто использовать

unsigned int flags = UINT_MAX;

Если вам нужны длинные биты, вы можете использовать

unsigned long flags = ULONG_MAX;

Эти значения гарантированно будут иметь все биты значений результирующего набора равными 1, независимо от того, как реализованы целые числа со знаком.

Я бы не стал делать вещь -1.Это довольно неинтуитивно (по крайней мере, для меня).Присвоение знаковых данных беззнаковой переменной кажется нарушением естественного порядка вещей.

В вашей ситуации я всегда использую 0xFFFF.(Конечно, используйте правильное количество F для переменного размера.)

[Кстати, я очень редко вижу трюк -1 в реальном коде.]

Кроме того, если вас действительно интересуют отдельные биты переменной, было бы неплохо начать использовать переменную с фиксированной шириной. uint8_t, uint16_t, uint32_t типы.

На процессорах Intel IA-32 можно записать 0xFFFFFFFF в 64-битный регистр и получить ожидаемые результаты.Это связано с тем, что IA32e (64-битное расширение IA32) поддерживает только 32-битные немедленные операции.В 64-битных инструкциях используются 32-битные непосредственные инструкции. расширенный знаком до 64-бит.

Незаконным является следующее:

mov rax, 0ffffffffffffffffh

Следующее помещает 64 единицы в RAX:

mov rax, 0ffffffffh

Для полноты картины ниже в нижней части RAX (он же EAX) помещаются 32 единицы:

mov eax, 0ffffffffh

И на самом деле у меня случались сбои в программах, когда я хотел записать 0xffffffff в 64-битную переменную, а вместо этого получал 0xffffffffffffffff.В C это будет:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

результат:

x is 0xffffffffffffffff

Я думал опубликовать это как комментарий ко всем ответам, в которых говорилось, что 0xFFFFFFFF предполагает 32 бита, но на него ответило так много людей, что я решил добавить это как отдельный ответ.

См. ответ Литба для очень четкого объяснения проблем.

Я не согласен с тем, что, строго говоря, ни в том, ни в другом случае нет никаких гарантий.Я не знаю ни одной архитектуры, которая не представляла бы беззнаковое значение «на единицу меньше двух в степени числа битов», поскольку все установленные биты, но вот что на самом деле говорит Стандарт (3.9.1/7 плюс примечание 44):

Представления целочисленных типов должны определять значения с использованием чисто двоичной системы счисления.[Примечание 44:] Позиционное представление целых чисел, в котором используются двоичные цифры 0 и 1, в котором значения, представленные последовательными битами, являются аддитивными, начинаются с 1 и умножаются на последовательную целую степень 2, за исключением, возможно, бита с высшая должность.

Это оставляет возможность того, что один из битов может быть чем угодно.

Практически:Да

Теоретически:Нет.

-1 = 0xFFFFFFFF (или любой другой размер int на вашей платформе) верно только для арифметики с дополнением до двух.На практике это будет работать, но существуют устаревшие машины (мэйнфреймы IBM и т. д.), на которых у вас есть фактический знаковый бит, а не представление с дополнением до двух.Предложенное вами решение ~0 должно работать везде.

Хотя 0xFFFF (или 0xFFFFFFFF, и т. д.) может быть легче читать, это может нарушить переносимость кода, который в противном случае был бы переносимым.Рассмотрим, например, библиотечную процедуру для подсчета количества элементов в структуре данных, в которых установлены определенные биты (точные биты задаются вызывающей стороной).Процедура может быть совершенно независимой от того, что представляют собой биты, но все равно должна иметь константу «все биты установлены».В таком случае -1 будет намного лучше, чем шестнадцатеричная константа, поскольку она будет работать с любым битовым размером.

Другая возможность, если typedef значение используется для битовой маски, следует использовать ~(bitMaskType)0;если битовая маска имеет только 16-битный тип, в этом выражении будет установлено только 16 бит (даже если в противном случае 'int' было бы 32 бита), но поскольку 16 бит - это все, что требуется, все должно быть в порядке предоставил что на самом деле используется соответствующий тип в приведении типов.

Кстати, выражения вида longvar &= ~[hex_constant] попадете в неприятную ситуацию, если шестнадцатеричная константа слишком велика, чтобы поместиться в int, но поместится в unsigned int.Если int составляет 16 бит, тогда longvar &= ~0x4000; или longvar &= ~0x10000;очистит один бит longvar, но longvar &= ~0x8000; очистит бит 15 и все биты выше него.Ценности, которые подходят int к типу будет применен оператор дополнения int, но результат будет расширен до long, установка старших битов.Значения, которые слишком велики для unsigned int к типу будет применен оператор дополнения long.Однако к значениям, находящимся между этими размерами, для ввода будет применяться оператор дополнения. unsigned int, который затем будет преобразован в тип long без расширения знака.

Как уже отмечали другие, -1 — это правильный способ создать целое число, которое будет преобразовано в беззнаковый тип со всеми битами, установленными в 1.Однако самое важное в C++ — использование правильных типов.Следовательно, правильный ответ на вашу проблему (включая ответ на заданный вами вопрос) таков:

std::bitset<32> const flags(-1);

Он всегда будет содержать точное количество бит, которое вам нужно.Он создает std::bitset со всеми битами, установленными в 1 по тем же причинам, которые указаны в других ответах.

Это, конечно, безопасно, так как при -1 всегда будут установлены все доступные биты, но мне больше нравится ~0.-1 просто не имеет особого смысла для unsigned int. 0xFF...это не хорошо, потому что это зависит от ширины шрифта.

Я говорю:

int x;
memset(&x, 0xFF, sizeof(int));

Это всегда даст вам желаемый результат.

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

Присвоение -1 работает для любого без подписи целочисленный тип (unsigned int, uint8_t, uint16_t и т. д.) как для C, так и для C++.

В качестве альтернативы для C++ вы можете:

  1. Включать <limits> и использовать std::numeric_limits< your_type >::max()
  2. Написать пользовательская шаблонная функция (Это также позволило бы провести некоторую проверку работоспособности, т.е.если тип назначения действительно является беззнаковым типом)

Целью может быть добавление большей ясности, поскольку назначение -1 всегда будут нуждаться в пояснительных комментариях.

Способ сделать значение более очевидным и при этом избежать повторения типа:

const auto flags = static_cast<unsigned int>(-1);

да, показанное представление очень правильное, так как если бы мы сделали это наоборот, вам потребовалось бы, чтобы оператор перевернул все биты, но в этом случае логика довольно проста, если мы учтем размер целых чисел в машине

например, в большинстве машин целое число составляет 2 байта = 16 бит, максимальное значение, которое оно может содержать, составляет 2 ^ 16-1 = 65535 2 ^ 16 = 65536

0%65536 = 0 -1%65536 = 65535, которые искажают до 1111 ............. 1, и все биты установлены на 1 (если мы рассмотрим классы остатков. Мод 65536), следовательно, это много простой.

Наверное

нет, если вы примете во внимание эту идею, она идеально подходит для беззнаковых целых чисел, и это действительно работает

просто проверьте следующий фрагмент программы

int main () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

ответ на b = 4294967295, что равно -1%2^32 для 4-байтовых целых чисел

следовательно, это совершенно справедливо для целых чисел без знака

в случае каких-либо несоответствий, пожалуйста, сообщите

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top