Как определить, является ли переменная без знака или нет в ANSI C - PullRequest
11 голосов
/ 06 сентября 2011

Я изучаю "Программирование на Эксперт С" Питера Ван Дер Линдена. В главе A.6 автор описал, как определить, является ли переменная без знака или нет в K & R C. Макрос приведен ниже:

#define ISUNSIGNED(a) (a>=0 && ~a>=0)

Книга очень старая, она была впервые опубликована в 1994 году! И я не изучал K & R C раньше. Вопрос в том, как определить, является ли переменная без знака или нет в ANSI C.

Я пытался решить проблему следующим образом. Так как в ANSI C "0" равно int, а любое другое число, кроме float, двойное и длинное двойное будет преобразовано в int или unsigned int с помощью Integer Upgrade при сравнении с 0. Поэтому я хочу найти грань между числом без знака и знаком. Когда я сравниваю (тип ребра) 0 с a, тип a не изменится. Макрос также модель ниже:

#define ISUNSIGNED(a) (a>=(the edge type)0 && ~a>=(the edge type)0)

Не могу найти тип ребра, есть кто-нибудь, кто может помочь мне решить проблему? Я изменил «число» на «переменную» для более точного выражения.

Ответы [ 5 ]

13 голосов
/ 06 сентября 2011

Как это работает

Переменная со знаком должна хранить свой знак в некотором бите. Обычно это самый важный, но это может быть любой из них. Переменная без знака не имеет знаковых битов; таким образом, самое низкое значение, которое оно может содержать, равно 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))), уступает тем же целочисленным повышения , Это может быть лучшее, что вы можете сделать (хотя я полагаю, что это лучше, чем ничего в тех случаях, когда вы этого хотите).

4 голосов
/ 06 сентября 2011

Вы не можете.

Подписанный long / int / short / char отрицателен, если установлен MSB. Без знака long / int / short / char НЕ отрицателен, если установлен MSB. В табличной форме:

          MSB=0  MSB=1
unsigned  +      +
signed    +      -

Поэтому ответ таков: подпись - это интерпретация . Один и тот же номер (последовательность байтов) может быть интерпретирован как подписанный или беззнаковый; Вы не можете решить, подписано ли число или нет, проверив его значение / содержание. Это то, что статическая типизация (также) для.

примечание: в комментариях было упомянуто, что C, вероятно, не предписывает MSB быть "знаком" для целочисленных типов со знаком. Это почти всегда так.

note2: первоначальная формулировка вопроса, задаваемого об определении подписи числа , а не переменной (отсюда мой ответ о интерпретации и статической типизации в C)

3 голосов
/ 09 марта 2012

#define ISUNSIGNED(a) (a>=0 && ((a=~a)>=0 ? (a=~a, 1) : (a=~a, 0)))

2 голосов
/ 06 сентября 2011
#define ISUNSIGNED(type) (((type)-1) >= 0)

или

#define ISUNSIGNED(a) (((typeof(a))-1) >= 0) // This is a GNU extension
0 голосов
/ 19 декабря 2018

Для всех, кто приходит к этому вопросу, но не ограничивается 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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...