Как определить, является ли переменная без знака или нет в ANSI C
Я изучаю "Expert C Programming" Питера Ван Дер Линдена. В главе A.6 автор описал, как определить, является ли переменная без знака или нет в K & R C. Макрос находится ниже:
#define ISUNSIGNED(a) (a>=0 && ~a>=0)
Книга очень старая, она была впервые опубликована в 1994 году! И я еще не изучил K & R C.
Вопрос заключается в том, как определить, является ли переменная без знака или нет в ANSI C.
Я попытался решить эту проблему. Так как "0" является int в ANSI C, а любое другое число, кроме float, double и long double, будет преобразовано в int или unsigned int с помощью Integer Upgrade при сравнении с 0. Поэтому я хочу найти ребро между неподписанным и подписанным числом. Когда я сравниваю (тип края) 0 с a, тип a не будет изменен. Макрос также приведен ниже:
#define ISUNSIGNED(a) (a>=(the edge type)0 && ~a>=(the edge type)0)
Я не могу найти тип края, есть ли кто-нибудь, кто может помочь мне решить проблему?
Я изменил "число" на "переменную" для более точного выражения.
Ответы
Ответ 1
Как это работает
Подписанная переменная должна хранить свой знак в некотором разряде. Обычно это самый важный, но это может быть любой из них. Беззнаковая переменная не имеет знаковых битов; таким образом, самое низкое значение, которое оно может удерживать, равно 0. Это означает, что для неподписанной переменной a
выражение a >= 0
всегда будет истинным.
Итак, мы имеем:
( a >= 0 && ~a >= 0 )
Если a
не имеет знака, первое значение истинно (оно должно быть), а второе - истинно (поскольку любое значение ~a
равно, оно все равно значение без знака, поэтому оно все равно >= 0
). Если a
подписано, это означает, что если бит знака установлен, a >= 0
является ложным (и выражение возвращает 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))
) уступает одному и тому же целому ряду продвижений. Это может быть лучшее, что вы можете сделать (хотя я полагаю, что это лучше, чем ничего в тех случаях, когда вы захотите).
Ответ 2
Вы не можете.
Подписанный long/int/short/char отрицательный, если установлен MSB.
Беззнаковый long/int/short/char НЕ является отрицательным, если установлен MSB.
В табличной форме:
MSB=0 MSB=1
unsigned + +
signed + -
Таким образом, ответ: подпись - это интерпретация. Такое же количество (последовательность байтов) может быть интерпретировано как подписанное или неподписанное; вы не можете решить, подписано ли число или нет, проверяя его значение/содержимое. То, что статическая типизация (также) для.
Примечание: в комментариях было упомянуто, что C, вероятно, не указывает MSB как "знак" для целочисленных типов со знаком. Это почти всегда верно.
note2: оригинальная постановка вопроса, заданная об определении знака числа, а не переменной (отсюда и мой ответ о интерпретации и статической типизации в C)
Ответ 3
#define ISUNSIGNED(a) (a>=0 && ((a=~a)>=0 ? (a=~a, 1) : (a=~a, 0)))
Ответ 4
#define ISUNSIGNED(type) (((type)-1) >= 0)
или
#define ISUNSIGNED(a) (((typeof(a))-1) >= 0) // This is a GNU extension
Ответ 5
Для всех, кто задает этот вопрос, но не ограничивается C89 (технически C11 также является ANSI C, потому что ANSI его ратифицировал):
#include <stdio.h>
#include <limits.h>
#define ISSIGNED(x) _Generic((x), \
unsigned char: 0, \
unsigned short: 0, \
unsigned int: 0, \
unsigned long: 0, \
unsigned long long: 0, \
signed char: 1, \
signed short: 1, \
signed int: 1, \
signed long: 1, \
signed long long: 1, \
char: (CHAR_MIN < 0) \
)
int main()
{
char x;
printf("%d\n", ISSIGNED(x));
}