【问题标题】:The best way to define a "between" macro in C在 C 中定义“介于”宏的最佳方法
【发布时间】:2011-11-28 07:54:41
【问题描述】:

定义一个 between 宏的最佳方法是什么,它是泛型类型 (char,int,long) 如果一个数字介于输入的其他数字之间,它将返回 true。 我试图用谷歌搜索它,但我没有找到任何东西。

编辑:给定的两个边界的顺序无关紧要。所以它可以更通用。

【问题讨论】:

  • 最好的方法? 不要那样做。 :)
  • 不要责怪C程序员;他们倾向于为所有内容定义宏。他们没有inline 函数。
  • 谁需要内联函数?函数有什么问题?
  • @Hossein:C 从 C99 开始就具有内联函数(在许多情况下,例如在使用 gcc 时)

标签: c coding-style macros


【解决方案1】:

如果你这样做:

#define BETWEEN(a, b, c)  (((a) >= (b)) && ((a) <= (c)))

a 的双重评估会有问题。想想如果你用一个有副作用的函数这样做会发生什么......

您应该改为:

#define BETWEEN(a, b, c) ({ __typeof__ (a) __a = (a); ((__a) >= (b) && ((__a) <= (c)) })

(已编辑,因为结果不应取决于 b 和 c 的顺序):

#define BETWEEN(a, b, c) \
           ({ __typeof__ (a) __a = (a);\
              __typeof__ (b) __b = (b);\
              __typeof__ (c) __c = (c);\
               (__a >= __b && __a <= __c)||\
               (__a >= __c && __a <= __b)})

【讨论】:

  • 是的,但是。 ({})__typeof__ 都是 gcc 扩展。
  • 是的,实际上我正在使用 C 的 IAR 扩展。所以这对我不起作用,但很好
  • 要使其在 Clang 中编译,请在最后一行的 __a &lt;= __b 之后添加分号。内置:Apple LLVM 6.0 版(clang-600.0.56)(基于 LLVM 3.5svn)
【解决方案2】:

首先,不要将宏用于此类事情 - 使用函数(可能是内联的)。

其次,如果您必须使用宏,那么例如有什么问题

#define BETWEEN(x, x_min, x_max) ((x) > (x_min) && (x) < (x_max))

?

根据您的后续编辑,如果您不知道 x_minx_max 的顺序,那么您可以这样做:

#define BETWEEN2(x, x0, x1) (BETWEEN((x), (x0), (x1)) || \
                             BETWEEN((x), (x1), (x0)))

关于宏和副作用等的常见警告适用。 编辑:删除宏和编译参数之间的空格

【讨论】:

  • 如果(x0)(x1) 的评估有副作用(或非常昂贵),特别是第二个版本有可能做RealBadStuff。第一次对(x) 的评估也是如此。我知道这就是您所说的“通常的警告” 的意思,但我不太确定阅读此代码的每个人都清楚。
  • 这会在使用 BETWEEN2 时引发以下编译错误:错误[Pe020]:标识符“x”未定义错误[Pe020]:标识符“x_min”未定义错误[Pe020]:标识符“x_max”为未定义
  • @Mellowcandle:这两个宏编译时没有警告我——也许你有错字——你能发布你的代码吗?这是一个工作示例:codepad.org/UFVAQA6j
  • @Mellowcandle:好的 - 我现在看到你解决了第一个宏中杂散空间的问题 - 谢谢。
【解决方案3】:

如果所有类型都相同,那么如何:

/* Check if 'a' is between 'b' and 'c') */
#define BETWEEN(a, b, c)  (((a) >= (b)) && ((a) <= (c)))

注意,如果上面abc的类型不同,C的隐式类型转换可能会出错。尤其是当您混合有符号和无符号数字时。

【讨论】:

  • @Mellowcandle - 好吧,当然不是。如果您希望它以两种方式工作,则需要添加逻辑 OR (||) 条件并重复检查 b & c 反转。
【解决方案4】:

+1:一个不那么简单的问题,因为它涉及到宏观参数的评估问题。天真的解决方案是

#define BETWEEN(x,l1,l2) (((x) >= (l1)) && ((x) <= (l2)))

但是“x”被计算了两次,所以如果表达式有副作用,可能会出现问题。让我们尝试一些更聪明的方法:

#define BETWEEN(x,l1,l2) (((unsigned long)((x)-(l1))) <= (l2))

非常非常棘手而且不那么干净,但是......

【讨论】:

  • 几乎,但它应该是(x) - (l1) &lt;= (l2) - (l1),你又在那里进行了双重评估。另外,您可能不会转换为unsigned long,因为如果它像BETWEEN(.05, 1e-4, 1e-3) 那样使用会怎样。
  • 不错的想法,但 (x) 和 (l1) 的数学运算可能不是整数:这严重分解了某些参数是指针类型
  • @sehe,我认为应该在这里省略指针。只有当您已经知道它们指向同一个周围对象时,比较指针才被明确定义。如果您通过上下文获得此类信息,那么无论如何,您甚至可能都不需要介数宏。
  • @Jens:用例是你知道它们都是指向同一个数组的指针,但你不知道在哪里。就像您可能将三个整数索引放入一个数组中,并希望对它们执行“介于”测试。知道它们都在同一个数组中并不能告诉你哪个在中间。
【解决方案5】:
#define BETWEEN_MIN(a, b) ((a)<(b) ? (a) : (b))
#define BETWEEN_MAX(a, b) ((a)>(b) ? (a) : (b))

#define BETWEEN_REAL(val, lo, hi) (((lo) <= (val)) && ((val) <= (hi)))
#define BETWEEN(val, hi, lo) \
      BETWEEN_REAL((val), BETWEEN_MIN((hi), (lo)), BETWEEN_MAX((hi), (lo)))

查看代码运行:http://ideone.com/Hb1vP

【讨论】:

  • +1 只是为了让我熟悉 ideone.com,太棒了。
【解决方案6】:

如果我理解正确,你需要3个数字,上限,下限和你要检查的数字,所以我会这样做:

#define BETWEEN(up, low, n) ((n) <= (up) && (n) >= (low))

这假设中间包含上限和下限,否则:

#define BETWEEN(up, low, n) ((n) < (up) && (n) > (low))

【讨论】:

  • 最合理的 C 编码指南要求宏以全部大写字母命名,尤其是当它们多次评估其参数时。
  • 或者就此而言,少于一次:low 是有条件地未评估的。
猜你喜欢
  • 1970-01-01
  • 2010-09-15
  • 2015-07-12
  • 2020-04-22
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
相关资源
最近更新 更多