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

Я видел, как этот шаблон много использовал в C и С++.

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

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

Ответ 1

Я рекомендую вам сделать это точно так, как вы показали, так как это самый прямой. Инициализируйте до -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 будет работать с каждым типом и машиной.

Ответ 2

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

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

Ответ 3

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

Ответ 4

Способом, позволяющим избежать упомянутых проблем, является просто:

unsigned int flags = 0;
flags = ~flags;

Portable и точка.

Ответ 5

Я не уверен, что использование unsigned int для флагов - это хорошая идея в первую очередь на С++. Как насчет битета и тому подобного?

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

Ответ 6

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);

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

Ответ 7

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

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

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

Ответ 8

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

unsigned int flags = UINT_MAX;

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

unsigned long flags = ULONG_MAX;

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

Ответ 9

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

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

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        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;

Ответ 10

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

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

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

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

Ответ 11

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

Неправильно следующее:

mov rax, 0ffffffffffffffffh

Ниже перечислены 64 1s в RAX:

mov rax, 0ffffffffh

Только для полноты, следующая позиция 32s в нижней части RAX (aka EAX):

mov eax, 0ffffffffh

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

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

результат:

x is 0xffffffffffffffff

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

Ответ 12

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

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

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

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

Ответ 13

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

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

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

Ответ 14

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

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

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

Ответ 15

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

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

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

Ответ 16

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

Ответ 17

Я говорю:

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

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

Ответ 18

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

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

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

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

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

Ответ 19

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

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

Ответ 20

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

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

0% 65536 = 0 -1% 65536 = 65535, который соответствует 1111............. 1, и все биты установлены в 1 (если мы рассмотрим классы вычетов mod 65536) поэтому он очень прямолинейный.

Думаю,

нет, если учесть это понятие, он отлично подходит для неподписанных ints и фактически работает

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

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