C中定义MIN
和MAX
的位置,如果有的话?
实现这些的最佳方式是什么,尽可能安全且类型安全? (首选编译器扩展/内置主流编译器。)
答案 0 :(得分:332)
C中定义
MIN
和MAX
的位置,如果有的话?
他们不是。
实现这些的最佳方法是什么,尽可能通用和类型安全(首选编译器扩展/内置主流编译器)。
作为功能。我不会使用像#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
这样的宏,特别是如果您计划部署代码。要么自己编写,要么使用标准fmax
或fmin
之类的内容,要么使用GCC's typeof来修复宏(你也获得了类型安全奖励):
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
每个人都说“哦,我知道双重评估,这没有问题”,几个月后,你将在几个小时内调试最愚蠢的问题。
请注意使用__typeof__
代替typeof
:
如果您正在编写头文件 必须在ISO C中包含时才能工作 程序,写
__typeof__
而不是typeof
。
答案 1 :(得分:84)
它也在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))
源代码库位于:
答案 2 :(得分:69)
C ++中有std::min
和std::max
,但是AFAIK,C标准库中没有等价物。您可以使用像
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
但如果你写一些像MAX(++a, ++b)
这样的东西,这会导致问题。
答案 3 :(得分:20)
避免使用非标准编译器扩展,并将其作为纯标准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 <stdio.h>
#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;
}
答案 4 :(得分:18)
我不认为它们是标准化的宏。已有标准化的浮点函数,fmax
和fmin
(fmaxf
表示浮点数,fmaxl
表示长双精度数。)
只要您了解副作用/双重评估的问题,就可以将它们实现为宏。
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
在大多数情况下,您可以将其留给编译器来确定您要执行的操作并尽可能地优化它。虽然这在像MAX(i++, j++)
这样使用时会导致问题,但我怀疑在一次检查中增加值的最大值是非常需要的。先增加,然后检查。
答案 5 :(得分:18)
由于最近的发展,这是一个迟到的答案。由于OP接受了依赖于非便携式GCC(和铿锵声)扩展typeof
- 或__typeof__
以便清除&#39; 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 ++代码中,尽管在使用C ++ 11时没有充分的理由不使用auto
的高级类型推断功能。
那就是说,我假设当宏被包含在extern "C" { ... }
范围内时,使用这种语法没有问题;例如,来自C头。 AFAIK,这个扩展没有找到它的方式info clang
答案 6 :(得分:11)
我写了这个version,适用于MSVC,GCC,C和C ++。
#if defined(__cplusplus) && !defined(__GNUC__)
# include <algorithm>
# define MIN std::min
# define MAX std::max
//# define TMIN(T, a, b) std::min<T>(a, b)
//# define TMAX(T, a, b) std::max<T>(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
答案 7 :(得分:8)
如果你需要min / max以避免昂贵的分支,你不应该使用三元运算符,因为它会编译成跳转。下面的链接描述了一种在没有分支的情况下实现最小/最大功能的有用方法。
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
答案 8 :(得分:3)
看起来Windef.h
(la #include <windows.h>
)有max
和min
(小写)宏,它们也遭受“双重评估”难度,但是那些不想重新推销自己的人:)
答案 9 :(得分:3)
我知道那个人说“C”...... 但如果有机会,请使用C ++模板:
template<class T> T min(T a, T b) { return a < b ? a : b; }
输入安全,其他评论中提到的++没有问题。
答案 10 :(得分:3)
值得指出的是,如果您使用第三级定义min
和max
,
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
然后为fmin(-0.0,0.0)
和fmax(-0.0,0.0)
的特殊情况得到相同的结果,你需要交换参数
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
答案 11 :(得分:3)
@David Titarenco nailed it here,但至少让我对其进行清理以使其看起来更好,并同时显示min()
和 max()
以进行复制从这里粘贴更容易。 :)
这项技术是常用的,受到那些知道如何正确使用它,“事实上”的做事方式的人的推崇,如果使用得当,可以使用,但 buggy (认为:double-evaluation side effect),如果您曾经通过包含变量赋值的表达式进行比较:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
此技术避免了上述“双重评估”的副作用和错误,因此被认为是实现此目的的更优越,更安全和“更现代”的 GCC C方法:
但是:请务必当心“ variable shadowing”效果,因为语句表达式显然是内联的,因此没有自己的局部变量作用域!
#define max(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
#define min(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})
请注意,在gcc语句表达式中,代码块中的最后一个表达式是从表达式“返回”的内容,就好像它是从函数返回的一样。 GCC's documentation这样说:
复合语句中的最后一件事应该是一个表达式,后跟一个分号;该子表达式的值用作整个构造的值。 (如果在花括号中最后使用某种其他类型的语句,则该构造的类型为void,因此实际上没有任何值。)
C ++注意:如果使用C ++,则可能建议使用模板代替这种构造。
答案 12 :(得分:1)
<?, >?, <?=, >?=
在一个非常旧的 GCC 版本中,有运算符 <?, >?
(参见 here,这里是 C++,但我认为它当时也用作 C 扩展)
我还看到了与赋值语句相对应的运算符 <?=, >?=
。
操作数被评估一次,甚至允许一个非常短的赋值语句。与常见的最小/最大分配相比,它非常短。没有什么可以超越这一点。
那些是以下内容的简写:
min(a, b) === a < b ? a : b === a <? b;
max(a, b) === a > b ? a : b === a >? b;
a = min(a, b); === if(b < a) a = b; === a <?= b;
a = max(a, b); === if(b > a) a = b; === a >?= b;
求最小值非常简洁:
int find_min(const int* ints, int num_ints)
{
assert(num_ints > 0);
int min = ints[0];
for(int i = 1; i < num_ints; ++i)
min <?= ints[i];
return min;
}
我希望有朝一日能把这件事带回 GCC,因为我认为这些运营商很天才。
答案 13 :(得分:0)
两个整数a
和b
的最大值为(int)(0.5((a+b)+abs(a-b)))
。这也适用于(double)
和fabs(a-b)
双打(类似于花车)
答案 14 :(得分:0)
与Brett Hale's comment有关,clang
在2016年左右开始支持__auto_type
(请参阅patch)。
答案 15 :(得分:-3)
最简单的方法是将其定义为.h
文件中的全局函数,并在您需要时调用它,如果您的程序是模块化的,包含大量文件。如果没有,double MIN(a,b){return (a<b?a:b)}
是最简单的方法。