Есть ли разница между -1 и ~ 0?
При сравнении значения без знака, как в этом тесте:
if (pos == (size_t)-1)
Это сравнение технически отличается от следующего:
if (pos == (size_t)~0)
Я не привык ко второму варианту. Вот почему я задаю вопрос. Ответ может быть довольно неудачным, если это да.
Ответы
Ответ 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) в операнде назначения и сохраняет результат в месте расположения операнда.
Моим выводом было бы, теоретически, код может отличаться на какой-то экзотической платформе и компиляторе, но в противном случае - нет. И всегда, если вы не уверены в сборе списка сборок на своей платформе.