在C中的位置MIN
和MAX
定义,如果有的话?
实现这些的最佳方式是什么,尽可能通用和安全?(首选编译器扩展/内置主流编译器.)
在C中的位置
MIN
和MAX
定义,如果有的话?
他们不是.
实现这些的最佳方法是什么,尽可能通用和类型安全(首选编译器扩展/内置主流编译器).
作为功能.我不会使用宏#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
,特别是如果您计划部署代码.无论是写自己的,使用类似标准fmax
或fmin
,或使用固定宏海湾合作委员会的typeof(你的类型安全奖励太):
#define max(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; })
每个人都说"哦,我知道双重评估,这没有问题",几个月后,你将在几个小时内调试最愚蠢的问题.
注意使用__typeof__
而不是typeof
:
如果您正在编写包含在ISO C程序中必须工作的头文件,请编写
__typeof__
而不是typeof
.
它也在GNU libc(Linux)和FreeBSD版本的sys/param.h中提供,并且具有dreamlax提供的定义.
关于Debian:
$ uname -sr Linux 2.6.11 $ cat /etc/debian_version 5.0.2 $ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) $ head -n 2 /usr/include/sys/param.h | grep GNU This file is part of the GNU C Library.
在FreeBSD上:
$ uname -sr FreeBSD 5.5-STABLE $ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b))
源存储库在这里:
GNU C库
FreeBSD的
有一个std::min
和std::max
C++,但AFAIK,在C标准库中没有等价物.您可以使用宏来自己定义它们
#define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define MIN(x, y) (((x) < (y)) ? (x) : (y))
但如果你写一些像这样的东西会导致问题MAX(++a, ++b)
.
避免使用非标准编译器扩展,并将其作为纯标准C(ISO 9899:2011)中完全类型安全的宏实现.
解
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y)) #define ENSURE_int(i) _Generic((i), int: (i)) #define ENSURE_float(f) _Generic((f), float: (f)) #define MAX(type, x, y) \ (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
用法
MAX(int, 2, 3)
说明
宏MAX根据type
参数创建另一个宏.如果针对给定类型实现此控制宏,则用于检查两个参数的类型是否正确.如果type
不支持,则会出现编译器错误.
如果x或y的类型不正确,则ENSURE_
宏中将出现编译器错误.如果支持更多类型,可以添加更多此类宏.我假设只使用算术类型(整数,浮点数,指针等),而不是结构或数组等.
如果所有类型都正确,则将调用GENERIC_MAX宏.每个宏参数都需要额外的括号,作为编写C宏时的常用标准预防措施.
然后是C中隐式类型促销的常见问题.?:
运算符将第2和第3个操作数相互平衡.例如,结果GENERIC_MAX(my_char1, my_char2)
将是一个int
.为了防止宏执行此类潜在危险类型的促销,使用了最终类型转换为预期类型.
合理
我们希望宏的两个参数都是相同的类型.如果其中一个类型不同,则宏不再是类型安全的,因为类似的运算符?:
将产生隐式类型促销.并且因为它确实如此,我们总是需要将最终结果转换回预期类型,如上所述.
只有一个参数的宏可以用更简单的方式编写.但是,如果有2个或更多参数,则需要包含额外的类型参数.因为这样的事情很遗憾是不可能的:
// this won't work #define MAX(x, y) \ _Generic((x), \ int: GENERIC_MAX(x, ENSURE_int(y)) \ float: GENERIC_MAX(x, ENSURE_float(y)) \ )
问题是如果上面的宏被调用为MAX(1, 2)
两个int
,它仍然会尝试宏扩展_Generic
关联列表的所有可能场景.所以ENSURE_float
宏也会得到扩展,即使它不相关int
.由于该宏有意只包含该float
类型,因此代码将无法编译.
为了解决这个问题,我在预处理器阶段使用##运算符创建了宏名称,这样就不会意外地扩展宏.
例子
#include#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y)) #define ENSURE_int(i) _Generic((i), int: (i)) #define ENSURE_float(f) _Generic((f), float: (f)) #define MAX(type, x, y) \ (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y)) int main (void) { int ia = 1, ib = 2; float fa = 3.0f, fb = 4.0f; double da = 5.0, db = 6.0; printf("%d\n", MAX(int, ia, ib)); // ok printf("%f\n", MAX(float, fa, fb)); // ok //printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong //printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong //printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong //printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong //printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either //printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either return 0; }
由于最近的发展,这是一个迟到的答案.由于OP接受了依赖于非便携式GCC(和clang)扩展的答案typeof
- 或者__typeof__
用于"干净"ISO C - 从gcc-4.9开始提供了更好的解决方案.
#define max(x,y) ( \ { __auto_type __x = (x); __auto_type __y = (y); \ __x > __y ? __x : __y; })
这个扩展的明显好处是每个宏参数只扩展一次,与__typeof__
解决方案不同.
__auto_type
是C++ 11的有限形式auto
.它不能(或不应该?)在C++代码中使用,尽管auto
在使用C++ 11时没有充分的理由不使用高级类型的推理功能.
也就是说,我认为当宏包含在extern "C" { ... }
作用域中时,使用此语法没有问题; 例如,来自C头.AFAIK,这个扩展没有找到它的方式信息clang
我不认为它们是标准化的宏.有浮点已经标准化的功能,fmax
并且fmin
(和fmaxf
对花车,和fmaxl
长期双打).
只要您了解副作用/双重评估的问题,就可以将它们实现为宏.
#define MAX(a,b) ((a) > (b) ? a : b) #define MIN(a,b) ((a) < (b) ? a : b)
在大多数情况下,您可以将其留给编译器来确定您要执行的操作并尽可能地优化它.虽然这在使用时会引起问题MAX(i++, j++)
,但我怀疑一次性检查增量值的最大值是非常需要的.先增加,然后检查.
我写了这个版本,对于MSVC,GCC,C和C++工程.
#if defined(__cplusplus) && !defined(__GNUC__) # include# define MIN std::min # define MAX std::max //# define TMIN(T, a, b) std::min (a, b) //# define TMAX(T, a, b) std::max (a, b) #else # define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \ ({ \ decltype(lexpr) lvar = (lexpr); \ decltype(rexpr) rvar = (rexpr); \ lvar binoper rvar ? lvar : rvar; \ }) # define _CHOOSE_VAR2(prefix, unique) prefix##unique # define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique) # define _CHOOSE(binoper, lexpr, rexpr) \ _CHOOSE2( \ binoper, \ lexpr, _CHOOSE_VAR(_left, __COUNTER__), \ rexpr, _CHOOSE_VAR(_right, __COUNTER__) \ ) # define MIN(a, b) _CHOOSE(<, a, b) # define MAX(a, b) _CHOOSE(>, a, b) #endif
如果你需要min/max以避免昂贵的分支,你不应该使用三元运算符,因为它会编译成跳转.下面的链接描述了一种在没有分支的情况下实现最小/最大功能的有用方法.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax