Как это работает
Переменная со знаком должна хранить свой знак в некотором бите. Обычно это самый важный, но это может быть любой из них. Переменная без знака не имеет знаковых битов; таким образом, самое низкое значение, которое оно может содержать, равно 0. Это означает, что для переменной без знака a
выражение a >= 0
будет всегда истинным.
Итак, мы имеем:
( a >= 0 && ~a >= 0 )
Если a
без знака, первое - это истина (должно быть), а второе - правда (потому что, какое бы значение ни было ~a
, оно все еще остается без знака, поэтому оно все равно >= 0
). Если a
подписано, это означает, что если бит знака установлен, a >= 0
имеет значение false (и выражение возвращает false, указывая, что эта переменная имеет тип со знаком). Если бит знака не установлен в a
, то когда ~a
инвертирует все биты в a
, бит знака (какой бы он ни был) имеет для быть установленным Это означает, что это должно быть отрицательное число, что означает, что ~a >= 0
возвращает false.
Это зависит от того, как стандартные целочисленные рекламные акции работают так, как вы ожидаете.
Как это не работает
unsigned char x = 1; // or whatever
printf("%s\n", ISUNSIGNED(x) ? "TRUE" : "FALSE"); // prints "FALSE"
Как кто-то еще указал, unsigned char
повышается до int
, поскольку любое значение ~a
для unsigned char a
может легко вписаться в диапазон int
. Возможно, это ошибка в стандартных целочисленных предложениях (или ошибка при наборе целочисленных литералов).
Может быть где-то еще реализация ISUNSIGNED
или ISSIGNED
, которая может преодолеть это ограничение. В библиотеке макросов P99 есть несколько изумительных применений макросов, многие из которых полагаются на вариационные макросы C99, но, к сожалению, макрос для проверки, подписано ли выражение или нет (#define SIGNED(expr) ((1 ? -1 : expr) < (1 ? 0 : expr))
), уступает тем же целочисленным повышения , Это может быть лучшее, что вы можете сделать (хотя я полагаю, что это лучше, чем ничего в тех случаях, когда вы этого хотите).