【问题标题】:How to detect signedness with a macro?如何使用宏检测签名?
【发布时间】:2013-08-19 23:17:09
【问题描述】:

我正在尝试检测整数类型系列的值(charunsigned charshortunsigned shortint,...)是否是 C 中的负数。如果可能的话一个可以用任何兼容的 C 编译器编译的宏(因此,不允许使用 gcc-tricks)并且没有警告

一段时间后,我带来了以下内容:

#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 技巧)的情况下消除所有这些警告?

【问题讨论】:

  • @WhozCraig:我说的不是变量的类型,而是变量的值。此外,给定一个变量,C 没有自省,因此无法“先验”获取变量的类型(除非您使用的是非标准扩展)。
  • (X &lt; 0) 有什么问题?是无符号类型的编译器警告吗?
  • 原谅我。开场白,“我正在尝试检测 C 中是否为整数类型”,让我大吃一惊。
  • @jxh:当 X 为 unsigned 时,它将在编译时引发警告。我不想发出警告,因为我们正在使用 -Werror 进行编译。

标签: c c-preprocessor signed


【解决方案1】:

这个定义似乎对我有用,不会产生任何警告:

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

我在IDEONE 上使用了你的测试用例和这个宏。

如果你的系统支持 C.11 的_Generic 选择功能,那么你可以做这样的事情(这有点简单,我相信可以通过利用积分提升规则来简化它):

#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)

【讨论】:

  • 我爱你的#define ISNEG(X) (!((X) &gt; 0) &amp;&amp; ((X) != 0))。这就是我所说的简单和优雅!谢谢 ! :-)
  • 谨防传入任何产生副作用的代码!
  • @gsk:是的,对于参数被多次展开的任何宏来说,这是一个问题。
  • @jxh:虽然这种担忧可以通过首先评估和存储 X 的结果,然后使用它来缓解(例如:typeof(X) X_ = X; (!(X_) > 0) && ((X_) != 0))
  • @gsk:是的,但是 OP 没有要求编译器扩展,而且语言是 C,而不是 C++。另请注意,OP 对原始宏中的宏参数进行了多达 4 次评估。
【解决方案2】:

对我来说,这很好用:

定义 ISNEG(X) ( (X)

有了这个结果 (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

【讨论】:

  • 当使用gcc -funsigned-char -W,然后在char变量上使用这个宏,我得到:warning: comparison is always false due to limited range of data type
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 2011-08-06
  • 2014-11-26
  • 2018-02-22
  • 2018-01-14
相关资源
最近更新 更多