以下哪一个宏是安全的,为什么?

时间:2014-01-23 06:24:33

标签: c gcc c-preprocessor

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

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

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

以上两者之间有什么区别。哪一个更好用,为什么。 我找到了这些宏的信息here。但无法理解。

3 个答案:

答案 0 :(得分:7)

第二个宏更安全,但使用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__)

但即使这样也不完全是防止失败的(运气不好;如果当时唯一的mymax(_a123,_b123)碰巧是123,可能会在__COUNTER__之类的调用中发生碰撞。)

实际上,使用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;它总是很有启发性的。

答案 1 :(得分:6)

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

),这一点很重要
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; })

答案 2 :(得分:2)

两者都不安全。请避免使用依赖于非标准C扩展(例如表达式语句)的华丽max宏实现,如果您必须将代码移植到其他平台,这将给您带来各种麻烦。我认为,max宏在我看来是由于其潜在的副作用而导致C标准库中最糟糕的事情之一。过去,为了安全起见,我{#undef编辑max

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

<强>替代

我的最爱:使用三元内联:a > b ? a : b,您可以放下括号,因为您确切知道发生了什么。通过获取有价值的副本,它也会比尝试安全的宏更快。

建立自己的功能。考虑为整数类型定义max,为浮点定义fmaxabsfabs已经有了先例。