Подтвердить что ты не робот

Есть ли разница между -1 и ~ 0?

При сравнении значения без знака, как в этом тесте:

if (pos == (size_t)-1)

Это сравнение технически отличается от следующего:

if (pos == (size_t)~0)

Я не привык ко второму варианту. Вот почему я задаю вопрос. Ответ может быть довольно неудачным, если это да.

4b9b3361

Ответ 1

Стандарт С++ гарантирует, что size_t является неподписанным типом, что неподписанные типы подчиняются обычным модульным арифметическим правилам (где модуль равен двоичному числу битов в представлении значений типа, см. 3.9/4), и поэтому -1, преобразованный в size_t, должен быть наибольшим значением, которое может представлять этот тип.

Значение 0 является int, а ~0 имеет все биты в представлении int с нулевым щелчком. Значение этого результата зависит от представления int на вашей платформе. Это значение (которое может быть ловушечным представлением, благодаря @Matt McNabb) затем преобразуется в size_t (что делается по правилам модульной арифметики).

В заключение, будет ли результирующее сравнение значений равным - это реализация. (Например, если int представляется в двух дополнениях, то значение ~0 равно -1, поэтому они одинаковы.)

Ответ 2

Предполагая (гарантированный стандартом), что size_t ссылается на целое значение без знака, это:

if(pos == (size_t)~0)

используется с намерением быть эквивалентным:

if(pos == (size_t)-1)

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

Ответ 3

Итак, в вашем примере технически нет никакой разницы. Потому что трудно найти компилятор, который не будет оптимизировать операции над литералами типа -1 и ~0. В вашем примере у меня есть точно:

        ; ...
        movq    $-1, -16(%rbp)
        movq    $-1, -8(%rbp)
        ; ...

Не бойтесь этих -1, assebmly is typeless;)

Более интересный вопрос: ваш пример:

#include <stddef.h>
int main() {
        int var0 = 0;
        int var1 = 1;
        size_t a = (size_t) -var1;
        size_t b = (size_t) ~var0;
        return a ^ b;
}

В моем случае (опция Kubuntu, gcc 4.8.2, x86_64, -O0) часть интереса была:

        movl    $0, -24(%rbp)    ; var0 = 0
        movl    $1, -20(%rbp)    ; var1 = 1

        movl    -20(%rbp), %eax
        negl    %eax             ; 2 complement negation

        ; ...

        movl    -24(%rbp), %eax
        notl    %eax             ; 1 complement negation

        ; ...

Взгляд в руководство Intel:

NEG - Отрицание двух дополнений

Заменяет значение операнда (операнда-адресата) своим два дополнения. (Эта операция эквивалентна вычитая операнд из 0.)

NOT - Отклонение одного дополнения

Выполняет побитовое НЕ-операцию (каждый 1 установлен в 0, и каждый 0 устанавливается в 1) в операнде назначения и сохраняет результат в месте расположения операнда.

Моим выводом было бы, теоретически, код может отличаться на какой-то экзотической платформе и компиляторе, но в противном случае - нет. И всегда, если вы не уверены в сборе списка сборок на своей платформе.