【问题标题】:Which one of the following macros is safe and why?以下哪个宏是安全的,为什么?
【发布时间】:2014-01-23 06:24:33
【问题描述】:

为了获得最多两个数字,我有以下宏

#define max(a,b) ((a) > (b) ? (a) : (b))

#define maxint(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

以上两者有什么区别。哪个更好用,为什么。 我找到了这些宏的信息here。但是看不懂。

【问题讨论】:

  • 想想预处理器用max(++i, --j)生成了什么。并且 都不 是“安全的”,但后者不太容易出现与宏参数扩展相关的副作用。
  • 第二个更安全。例如致电max(i++,j++) 或就此而言,max(fn1(),fn2())
  • 请注意,第二个宏使用gcc 扩展(将块语句视为表达式),因此不可移植。
  • 查看您提供的参考资料“#define max(a,b) ((a) > (b) ? (a) : (b)) 但是这个定义计算 a 或 b 两次,用如果操作数有副作用,结果会很糟糕。”
  • @Chinna,考虑去掉“linux”标签吗?

标签: c gcc c-preprocessor


【解决方案1】:

第二个宏更安全,但使用了 GCC 提供的非标准 C 扩展:statement expressions。但是第一个表达式是“通用的”。使用 GCC 的 typeof 扩展名会有所帮助:

 #define mymax(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \
                      _a > _b ? _a : _b; })

要解决JaredPar's answer 中提出的问题,您可以使用预处理器concatenation 和特定于GCC 的__COUNTER__(或者只是更标准的__LINE__),例如

 #define mymax_counted(a,b,c) ({typeof(a) _a##c = (a); \
                                typeof(b) _b##c = (b); \
                                _a##c > _b##c ? _a##c : _b##c; })
 #define mymax(a,b) mymax_counted(a,b,__COUNTER__)

但即使这样也不是完全防故障的(运气不好;如果当时唯一的 __COUNTER__ 恰好是 123,可能会在像 mymax(_a123,_b123) 这样的调用中发生冲突)。

实际上,使用inline function 更好(因为如果您调用mymaxfun(i++,t[i]),则行为已明确定义并给出与mymaxfun(t[i],i++) 相同的结果,并且因为优化编译器会产生与使用宏时一样高效的代码) :

static inline int mymaxfun(int a, int b) { return (a>b)?a:b; }

遗憾的是,C 没有通用函数(考虑切换到 C++ 及其 templates 并使用 std::max);但是C11 有使用_Generic 关键字的泛型表达式

宏很有用(在掌握之后),但是在调用宏时你应该非常小心参数中的副作用(例如mymax(i++,t[--i]++)),所以如果名称是宏或其他东西(如函数)。根据经验,避免副作用 - 特别是 ++-- 以及许多其他 - 在函数调用外观表达式(函数调用和宏调用)中。

查看源代码的预处理器扩展形式;所以对于foo.c 源代码,运行gcc -C -E foo.c > foo.i(添加任何预处理器选项,如-I-D 等...相关)并查看foo.i 内部,例如与less foo.i;它总是很有启发性。

【讨论】:

  • 唯一的问题是当有人试图将它与浮点数一起使用时。
【解决方案2】:

第二个宏更安全,因为它只对输入进行一次评估。例如,如果输入是具有副作用的表达式,这一点很重要

max(i++, --j); 

请注意,我说的是安全而不是安全。该宏仍有可能不正确,因为范围内可能存在已命名为 _a_b 的本地变量。想象一下如果执行以下操作会发生什么

int _a = 42;
int _b = 13;
maxint(_a, _b);

它会扩展到

int _a = 42;
int _b = 13;
{int _a = (_a), _b = (_b); _a > _b ? _a : _b; })

【讨论】:

  • 好点,但我注意到,出于这个确切原因,以下划线开头的局部变量是一种非常糟糕的编程习惯。
【解决方案3】:

两者都不安全。请避免使用浮华的 max 宏实现,这些宏实现依赖于像 表达式语句 这样的非标准 C 扩展,如果您不得不将代码移植到不同的平台,这会让您感到头疼。在我看来,max 宏是 C 标准库中最糟糕的事情之一,因为它有潜在的副作用。在过去,为了安全起见,我#undefed max

最好将第二个宏 maxint 编码为函数,因为它具有宏的所有缺点(例如,无法轻松调试,实例化变量与本地变量冲突)但没有任何好处(例如泛化)。

替代方案

我最喜欢的:使用三元内联:a > b ? a : b,因为你知道发生了什么,所以你可以去掉括号来品尝。它也将比通过获取值副本来尝试安全的宏更快。

构建您自己的函数。考虑为整数类型定义max,为浮点定义fmaxabsfabs 这里已经有先例了。

【讨论】:

    猜你喜欢
    • 2013-07-31
    • 2021-07-14
    • 2014-09-22
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    相关资源
    最近更新 更多