C 下列哪个宏是安全的,为什么?
为了获得最多两个数字,我有以下宏C 下列哪个宏是安全的,为什么?,c,gcc,c-preprocessor,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; }) 以上两者的区别是什么。哪一个更好用,为什么。 我找到了这些宏的信息。但是无法理解它。第二个宏更安全,因为它只对输入求值一次。例如,如果输入是具有副作用的表达式,这一点很重要 max(i++, --j); 注意,我说的是更安全和不
#define max(a,b) ((a) > (b) ? (a) : (b))
#define maxint(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
以上两者的区别是什么。哪一个更好用,为什么。
我找到了这些宏的信息。但是无法理解它。第二个宏更安全,因为它只对输入求值一次。例如,如果输入是具有副作用的表达式,这一点很重要
max(i++, --j);
注意,我说的是更安全和不安全。此宏仍然可能不正确,因为作用域中可能存在已命名为\u a
和\u
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; })
第二个宏更安全,但使用GCC提供的非标准C扩展:。但第一个表达是“泛型”。使用GCC的扩展将有助于:
#define mymax(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \
_a > _b ? _a : _b; })
为了解决中提出的问题,您可以使用预处理器和特定于GCC的(或者更标准的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
但即使这样也不是完全防故障的(运气不好;如果唯一的\uuuu计数器恰好是123,则可能在一些调用中发生冲突,如mymax(\ua123,\ub123)
)
实际上,使用a更好(因为如果调用mymaxfun(i++,t[i])
,行为定义良好,并给出与mymaxfun(t[i],i++)
相同的结果,并且因为优化编译器将生成与使用宏时一样高效的代码):
可悲的是,C没有泛型函数(考虑切换到它的名称和用法);但是,具有使用\u generic
关键字的类型泛型表达式
宏是有用的(掌握时),但在调用宏(例如,mymax(i++,t[--i]++)
)时,您应该非常小心参数中的副作用,因此您应该始终注意并记录名称是宏还是其他名称(如函数)。根据经验法则,避免在函数调用表达式(函数调用和宏调用)中产生副作用,尤其是++
或--
,还有许多其他副作用
查看源代码的预处理器扩展形式;因此,对于foo.c
源代码,运行gcc-c-efoo.c>foo.i
(添加任何相关的预处理器选项,如-i
,-D
等),查看foo.i
内部,例如使用更少的foo.i
;这总是很有启发性的。两者都不安全。请避免使用华而不实的max
宏实现,这些宏实现依赖于非标准的C扩展(如表达式语句),如果您不得不将代码移植到其他平台,这将给您带来各种各样的麻烦。在我看来,max
宏是C标准库中最糟糕的东西之一,因为它有潜在的副作用。过去,为了安全起见,我一直#unde
edmax
您最好将第二个宏maxint
编码为函数,因为它具有宏的所有缺点(例如无法轻松调试,实例化变量与局部变量冲突),但没有任何好处(例如泛化)
备选方案:
我的最爱:使用三元内联:a>b?a:b
在这里你可以放上括号来品尝,因为你知道到底发生了什么。它还将比试图通过复制值来确保安全的宏更快
构建自己的功能。考虑为整数类型定义<代码> max >代码>浮点上的代码>代码> fmax < /c>。这里已经有一个关于abs
和fabs
的先例,考虑一下使用max(++i,--j)
的预处理器生成了什么。两者都不是“安全的”,但后者不太容易产生与宏参数扩展相关的副作用。e、 g.调用max(i++,j++)
或者为此,max(fn1(),fn2())
注意,这些宏中的第二个使用了gcc
扩展(将块语句视为表达式),因此不可移植。请参阅您提供的参考“#定义max(a,b)((a)>(b);(a):(b))但是这个定义计算两个A或B,如果操作数有副作用,结果不好。“@ Chinna,考虑删除‘Linux’标签吗?唯一的问题是当有人试图用浮动来使用它的时候。好点,虽然我注意到用一个下划线来开始一个局部变量是一个非常糟糕的编程实践。正是因为这个原因。
#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__)
static inline int mymaxfun(int a, int b) { return (a>b)?a:b; }