检查符号未知的变量是否在间隔中

  • 本文关键字:是否 变量 符号 未知 c
  • 更新时间 :
  • 英文 :


在某些 C99 代码中,我需要检查变量 i 是否在已知max为正的区间[0, max]。问题是变量的类型允许有符号和无符号(通过更改typedef(。如何最好地检查变量是否在区间中?

直接的方法是:

bool is_in_interval(my_type i, my_type max) {
    assert(max > 0);
    return (i >= 0 && i <= max);
}

当我们有typedef int my_type;时,这将正常工作。但是当my_type是无符号的(即typedef unsigned int my_type;(,i >= 0总是正确的,编译器会(正确地(警告它,我想避免这种情况。(我不想关闭该警告,因为当这种比较实际上是无意的时,它很有用,并且忽略编译器警告不是一个好主意。

我目前的想法是将i转换为无符号类型,然后只检查上限:

bool is_in_interval(my_type i, my_type max) {
    assert(max > 0);
    return ((size_t) i <= (size_t) max);
}

如果有符号类型具有 2 的补码表示形式,则任何负值都应大于 max 一旦强制转换为无符号版本,对吗?如果是这样,这应该有效。但是,我不确定这是否是一种可取的方法。例如,假设有符号类型在所有平台上都使用 2 的补码是否安全?有没有更好的方法可以做到这一点?

检查 0 和 max 。 但避免直接>= 0测试。

强制转换是一个问题,因为它可能会以某种实现定义的方式缩小值。

bool is_in_interval(my_type i, my_type max) {
  // first, take care of the easy case
  if (i > max) return false;
  if (i > 0) return true;
  return i == 0;
}

我也会再考虑一下。

我认为这应该关闭编译器:

bool is_in_interval(my_type i, my_type max) {
  my_type zero = 0;
  assert(max > 0);
  return (zero <= i && i <= max);
}

也许这是另一种简单的方法:

bool is_in_interval(my_type i, my_type max) {
    assert(max > 0);
    if ((my_type)-1 > 0)
        return (i <= max); // my_type is unsigned
    else
        return (0 <= i && i <= max); // my_type is signed
}

一个简单的解决方案是进行 2 侧测试。

真正失去的唯一属性是适度的效率/性能。 没有其他问题。

尽管OP肯定考虑过这一点,但任何解决方案都应以此为参考。

bool is_in_interval2(my_type i, my_type min, my_type max) {
  return i >= min && i <= max;
}
// Could make this a macro or inline
bool is_in_interval(my_type i, my_type max) {
  return is_in_interval2(i, 0, max);
}

如果您的编译器支持它,我相信最干净的解决方案是保持代码原样,但使用 #pragma 指令在本地禁用虚假警告。 例如,对于 GCC 4.6.4+,以下内容就可以解决问题:

bool is_in_interval(my_type i, my_type max) {
    assert(max > 0);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
    /* i >= 0 could warn if my_type is unsigned */
    return (i >= 0 && i <= max);
#pragma GCC diagnostic pop
}

显然,完全相同的#pragma也应该适用于 Clang,因为它模仿了 GCC 的语法。 其他编译器可能具有类似的语法(例如 #pragma warning( disable: ... ) Visual C/C++(,并且应该可以使用预处理器指令为每个编译器选择适当的#pragma

这种方法的主要缺点是美观——它在代码中散落着丑陋的#pragma。 也就是说,我认为这比故意混淆(并可能取消优化(代码以避免编译器警告更可取。 至少,有了#pragma,下一个阅读代码的程序员很清楚为什么它是这样写的。


为了使它更清晰、更便携,您可以使用 C99 _Pragma() 运算符来定义禁用和重新启用警告的宏,例如:

#if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40604
#  define NO_TYPE_LIMIT_WARNINGS 
       _Pragma("GCC diagnostic push") 
       _Pragma("GCC diagnostic ignored "-Wtype-limits"")
#  define RESTORE_WARNINGS 
       _Pragma("GCC diagnostic pop")
/* Add checks for other compilers here! */
#else
#  define NO_TYPE_LIMIT_WARNINGS /* nothing */
#  define RESTORE_WARNINGS /* nothing */
#endif

并像这样使用它们:

bool is_in_interval(my_type i, my_type max) {
    assert(max > 0);
    NO_TYPE_LIMIT_WARNINGS; /* i >= 0 could warn if my_type is unsigned */
    return (i >= 0 && i <= max);
    RESTORE_WARNINGS;
}

或者,如果要支持 C99 之前的编译器,可以创建一对名为 warnings_off.hwarnings_restore.h 的头文件(没有任何包含保护!(,其中包含每个编译器的相应#pragma,然后将要使其警告静音的代码括起来:

#include "warnings_off.h"
/* ...code that emits spurious warnings here... */
#include "warnings_restore.h"

最新更新