2013-08-19 2 views
5

Я пытаюсь обнаружить, если значение целого типа семейства (char, unsigned char, short, unsigned short, int, ...) является отрицательным числом С. Если возможно с помощью макроса, который может быть собран с любым совместимым компилятором (так, нет разрешенных gcc) и без предупреждения!Как обнаружить подпись с макросом?

Через некоторое время я пришел с следующее:

#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 

Я пробовал со следующими примерами:

void 
display_result(int arg, int result) 
{ 
    printf("ISNEG(%d) is %stive\n", arg, (result ? "nega" : "posi")); 
} 

void 
display_uresult(unsigned int arg, int result) 
{ 
    printf("ISNEG(%u) is %stive\n", arg, (result ? "nega" : "posi")); 
} 

int main() 
{ 
    short shrt = 5; 
    short nshrt = -5; 
    unsigned short ushrt = 5; 

    display_result(shrt, ISNEG(shrt)); 
    display_result(nshrt, ISNEG(nshrt)); 
    display_uresult(ushrt, ISNEG(ushrt)); 

    int ni = -5; 
    int i = 5; 
    int zero = 0; 

    display_result(ni, ISNEG(ni)); 
    display_result(i, ISNEG(i)); 
    display_result(zero, ISNEG(zero)); 
    display_result(~zero, ISNEG(~zero)); // wrong 

    unsigned int uzero = 0; 
    unsigned int ui = 5; 

    display_uresult(uzero, ISNEG(uzero)); 
    display_uresult(~uzero, ISNEG(~uzero)); 
    display_uresult(ui, ISNEG(ui)); 

    long int li = -5; 
    unsigned long int uli = 5; 

    display_result(li, ISNEG(li)); 
    display_uresult(uli, ISNEG(uli)); 

    long long int lli = -5; 
    unsigned long long int ulli = 5; 

    display_result(lli, ISNEG(lli)); 
    display_uresult(ulli, ISNEG(ulli)); 

    return EXIT_SUCCESS; 
} 

И результат:

ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(0) is positive 
ISNEG(-1) is negative 
ISNEG(0) is positive 
ISNEG(4294967295) is positive 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 

Он работает довольно неплохо, но проблема в том, что при составлении всех предупреждений (-Wall -Wextra) я получаю следующее Крыло сообщение:

signedness.c: In function ‘main’: 
signedness.c:27:3: warning: promoted ~unsigned is always non-zero [-Wsign-compare] 
    display_uresult(ushrt, ISNEG(ushrt)); 
^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 
#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 
               ^
signedness.c:41:26: note: in expansion of macro ‘ISNEG’ 
    display_uresult(uzero, ISNEG(uzero)); 
         ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 
#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 
               ^
signedness.c:42:27: note: in expansion of macro ‘ISNEG’ 
    display_uresult(~uzero, ISNEG(~uzero)); 
         ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 
#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 
               ^
signedness.c:43:23: note: in expansion of macro ‘ISNEG’ 
    display_uresult(ui, ISNEG(ui)); 
        ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 
#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 
               ^
signedness.c:49:24: note: in expansion of macro ‘ISNEG’ 
    display_uresult(uli, ISNEG(uli)); 
         ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 
#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0))) 
              ^
signedness.c:55:25: note: in expansion of macro ‘ISNEG’ 
    display_uresult(ulli, ISNEG(ulli)); 
         ^

Итак, мои вопросы:

  1. Есть ли лучший способ определить, что мы имеем отрицательную переменные среди всех возможных целочисленных типов языка C?

  2. Как избавиться от всех этих предупреждений без его деактивации (и без использования трюков GCC)?

+0

Возможный дубликат [Макро, чтобы проверить, является ли заданный целочисленный тип подписанным или незаписанным в C?] (Http://stackoverflow.com/questions/7962855/macro-to-test-whether-a-given- integer-type-is-signed-or-unsigned-in-c) – WhozCraig

+0

@WhozCraig: Я не говорю о типе переменной, а о значении переменной. Более того, учитывая переменную, C не имеет интроспекции, поэтому нет способа получить тип переменной «априори» (за исключением случаев, когда вы используете нестандартное расширение). – perror

+0

Что не так с '(X <0)'? Это предупреждение компилятора для неподписанных типов? – jxh

ответ

5

Это определение, кажется, работает для меня без каких-либо предупреждений генерируется:

#define ISNEG(X) (!((X) > 0) && ((X) != 0)) 

Я использовал свои тестовые случаи с макросом на IDEONE.

Если вы на системе, которая поддерживает функцию _Generic выбора С.11, тогда вы можете сделать что-то вроде этого (это своего рода наивным, я уверен, что это может быть упрощена за счет использования интегральных правил продвижения по службе):

#define ISNEG(X) \ 
    _Generic((X), \     
      char: !((X) > 0) && (X) != 0, \ 
      signed char: (X) < 0, \ 
      short: (X) < 0, \ 
      int: (X) < 0, \ 
      long: (X) < 0, \ 
      long long: (X) < 0, \ 
      float: (X) < 0, \ 
      double: (X) < 0, \ 
      long double: (X) < 0, \ 
      default: 0) 
+0

Мне нравится ваш # #define ISNEG (X) (! ((X)> 0) && ((X)! = 0)) '. Это то, что я называю простым и элегантным *! Благодаря ! :-) – perror

+0

Остерегайтесь передачи каких-либо побочных эффектов, генерирующих код! – gsk

+0

@gsk: Да, это проблема для любого макроса, где аргумент расширяется более одного раза. – jxh

1

для меня это работало просто отлично:

определяют ISNEG (X) ((X) < ((ИНТ) 0))

с этим результатом (http://ideone.com/9SIoHQ)

ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(0) is positive 
ISNEG(-1) is negative 
ISNEG(0) is positive 
ISNEG(4294967295) is positive 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
ISNEG(-5) is negative 
ISNEG(5) is positive 
+0

При использовании 'gcc -funsigned-char -W', затем используя этот макрос в переменной' char', я получаю: 'предупреждение: сравнение всегда неверно из-за ограниченного диапазона типов данных' – jxh

Смежные вопросы