Безопасно ли использовать -1, чтобы установить все биты в true? - PullRequest
129 голосов
/ 01 мая 2009

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

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

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

Ответы [ 20 ]

2 голосов
/ 02 мая 2009

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

Недопустимо следующее:

mov rax, 0ffffffffffffffffh

Следующее ставит 64 1 с в RAX:

mov rax, 0ffffffffh

Просто для полноты, следующее помещает 32 1 в нижнюю часть RAX (он же EAX):

mov eax, 0ffffffffh

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

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

результат:

x is 0xffffffffffffffff

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

2 голосов
/ 12 мая 2009

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

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

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

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

1 голос
/ 12 декабря 2012

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

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

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

1 голос
/ 01 мая 2009

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

Теоретически: №

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

1 голос
/ 01 июня 2011

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

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

Кстати, выражения вида 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 без расширения знака.

0 голосов
/ 11 октября 2010

Я говорю:

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

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

0 голосов
/ 11 октября 2010

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

Например, на большинстве машин целое число составляет 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-байтовых целых чисел

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

в случае любых расхождений, plzz report

0 голосов
/ 06 октября 2016

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

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

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

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

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

0 голосов
/ 11 апреля 2019

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

const auto flags = static_cast<unsigned int>(-1);
0 голосов
/ 01 мая 2009

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

...