Как определить, является ли переменная без знака или нет в 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));
}