C 中的 MIN 和 MAX
C 中的 MIN
和 MAX
定义在哪里(如果有的话)?
实现这些的最佳方法是什么,尽可能通用并安全地输入? (首选主流编译器的编译器扩展/内置函数。)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
C 中的 MIN
和 MAX
定义在哪里(如果有的话)?
实现这些的最佳方法是什么,尽可能通用并安全地输入? (首选主流编译器的编译器扩展/内置函数。)
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(17)
他们不是。
作为函数。我不会使用像
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
这样的宏,特别是如果您计划部署您的代码。要么自己写,使用标准的fmax
或fmin
,或修复使用 GCC 的 typeof 的宏(您将获得类型安全奖励也)在GCC语句表达式中:每个人都说“哦,我知道双重评估,这没问题”,几个月后,您将连续几个小时调试最愚蠢的问题。
请注意使用
__typeof__
而不是typeof
:They aren't.
As functions. I wouldn't use macros like
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, especially if you plan to deploy your code. Either write your own, use something like standardfmax
orfmin
, or fix the macro using GCC's typeof (you get typesafety bonus too) in a GCC statement expression:Everyone says "oh I know about double evaluation, it's no problem" and a few months down the road, you'll be debugging the silliest problems for hours on end.
Note the use of
__typeof__
instead oftypeof
:它也在 GNU libc (Linux) 和 FreeBSD 版本的
sys/param.h
中提供,并且具有由 dreamlax 提供的定义。在 Debian 上:
在 FreeBSD 上:
源存储库位于:
It's also provided in the GNU libc (Linux) and FreeBSD versions of
sys/param.h
, and has the definition provided by dreamlax.On Debian:
On FreeBSD:
The source repositories are here:
C++ 中有
std::min
和std::max
,但据我所知,C 标准库中没有等效项。您可以使用类似这样的宏自行定义它们,但是如果您编写类似
MAX(++a, ++b)
的内容,则会出现问题。There's a
std::min
andstd::max
in C++, but AFAIK, there's no equivalent in the C standard library. You can define them yourself with macros likeBut this causes problems if you write something like
MAX(++a, ++b)
.@David Titarenco 将其钉在此处,但至少让我稍微清理一下以使其看起来不错,并显示两者
min()
和max()
一起使用,可以更轻松地从此处进行复制和粘贴。 :)2020 年 4 月 25 日更新:我还添加了第 3 节来展示如何使用 C++ 模板来完成此操作,对于同时学习 C 和 C++ 或从一种过渡到另一种的人来说,这是一个有价值的比较。我已尽力做到彻底、真实和正确,使这个答案成为我可以一次又一次回顾的规范参考,我希望你发现它和我一样有用。
1.旧的C宏方式:
这种技术很常用,受到那些知道如何正确使用它的人的尊重,“事实上”的做事方式,如果使用得当的话很好用,但是有bug (想想:双重计算副作用)如果你曾经传递包含变量赋值的表达式 进行比较:
2. 新的和改进的 gcc 和 clang "语句表达式”方式:
这种技术避免了上述“双重求值”的副作用和错误,因此被认为是更优越、更安全、“更现代”的GCC C方式做这个。期望它能够与 gcc 和 clang 编译器一起使用,因为 clang 在设计上是与 gcc 兼容的(请参阅本答案底部的 clang 注释)。
但是:请注意“变量遮蔽”仍然有效,因为语句表达式显然是内联的,因此没有自己的局部变量范围!
请注意,在 gcc 语句表达式中,代码块中的最后一个表达式是从表达式“返回”,就像从函数返回一样。 GCC 文档是这样说的:
3. [仅限 C++] C++ 模板方式:
C++ 注意:如果使用 C++,模板为可能建议使用这种类型的构造,但我个人不喜欢模板,并且可能会在 C++ 中使用上述构造之一,因为我也经常在嵌入式 C++ 中使用并更喜欢 C 样式。
本节添加于 2020 年 4 月 25 日:
在过去的几个月里,我一直在做大量的 C++ 工作,在 C++ 社区中,在可能的情况下,选择模板而不是宏的压力非常大。因此,我越来越擅长使用模板,并希望在此处放入 C++ 模板版本以保持完整性,并使之成为更规范和彻底的答案。
以下是
max()
和min()
的基本函数模板版本在 C++ 中的样子:在此处进一步阅读有关 C++ 模板的内容:< a href="https://en.wikipedia.org/wiki/Template_(C%2B%2B)" rel="noreferrer">维基百科:模板 (C++)。
但是, 标头 (
max()
和min()
已经是 C++ 标准库的一部分,位于 < code>#include
)。在 C++ 标准库中,它们的定义与上面的定义略有不同。例如,在 C++14 中,std::max<>()
和std::min<>()
的默认原型,查看它们的上面 cplusplus.com 链接中的原型是:请注意,关键字
typename
是class
的别名(因此,无论您说>
或
),因为后来在 C++ 模板发明后才承认,模板类型可能是常规类型(int
、float
等),而不仅仅是类类型。这里您可以看到,输入类型和返回类型都是
const T&
,意思是“对类型T
的常量引用”。这意味着输入参数和返回值是通过引用传递,而不是通过值传递。这就像传递指针一样,对于大型类型(例如类对象)更有效。函数的constexpr
部分 修改函数本身并指示函数必须能够在编译时进行计算(至少如果提供了constexpr
输入参数),但如果无法在编译时求值,则它默认返回运行时求值,就像任何其他普通函数一样。constexpr
C++ 函数的编译时方面使其类似于 C 宏,因为如果constexpr
函数可以进行编译时评估,则它将在编译时完成,与MIN()
或MAX()
宏替换相同,也可以在 C 或 C++ 中的编译时进行完全评估。有关此 C++ 模板信息的其他参考,请参阅下文。4. [仅限 C++] C++
std::max()
如果使用 C++,我想添加内置的
std::max()
函数有多种的形式。请参阅 cppreference.com 社区 wiki (https: //en.cppreference.com/w/cpp/algorithm/max),了解 4 种形式的std::max()
的 4 种可能实现。正常用法包括:
...但是如果您想一次比较多个数字,可以使用第四种形式,它接受
std::initializer_list
,像这样:函数声明:
用法:
引用:
Clang 注释 来自维基百科:
相关:
@David Titarenco nailed it here, but let me at least clean it up a bit to make it look nice, and show both
min()
andmax()
together to make copying and pasting from here easier. :)Update 25 Apr. 2020: I've also added a Section 3 to show how this would be done with C++ templates too, as a valuable comparison for those learning both C and C++, or transitioning from one to the other. I've done my best to be thorough and factual and correct to make this answer a canonical reference I can come back to again and again, and I hope you find it as useful as I do.
1. The old C macro way:
This technique is commonly used, well-respected by those who know how to use it properly, the "de facto" way of doing things, and fine to use if used properly, but buggy (think: double-evaluation side effect) if you ever pass expressions including variable assignment in to compare:
2. The new and improved gcc and clang "statement expression" way:
This technique avoids the above "double-evaluation" side effects and bugs, and is therefore considered the superior, safer, and "more modern" GCC C way to do this. Expect it to work with both the gcc and clang compilers, since clang is, by design, gcc-compatible (see the clang note at the bottom of this answer).
BUT: DO watch out for "variable shadowing" effects still, as statement expressions are apparently inlined and therefore do NOT have their own local variable scope!
Note that in gcc statement expressions, the last expression in the code block is what is "returned" from the expression, as though it was returned from a function. GCC's documentation says it this way:
3. [C++ only] The C++ template way:
C++ Note: if using C++, templates are probably recommended for this type of construct instead, but I personally dislike templates and would probably use one of the above constructs in C++ anyway, as I frequently use and prefer C styles in embedded C++ as well.
This section added 25 Apr. 2020:
I've been doing a ton of C++ the past few months, and the pressure to prefer templates over macros, where able, in the C++ community is quite strong. As a result, I've been getting better at using templates, and want to put in the C++ template versions here for completeness and to make this a more canonical and thorough answer.
Here's what basic function template versions of
max()
andmin()
might look like in C++:Do additional reading about C++ templates here: Wikipedia: Template (C++).
However, both
max()
andmin()
are already part of the C++ standard library, in the<algorithm>
header (#include <algorithm>
). In the C++ standard library they are defined slightly differently than I have them above. The default prototypes forstd::max<>()
andstd::min<>()
, for instance, in C++14, looking at their prototypes in the cplusplus.com links just above, are:Note that the keyword
typename
is an alias toclass
(so their usage is identical whether you say<typename T>
or<class T>
), since it was later acknowledged after the invention of C++ templates, that the template type might be a regular type (int
,float
, etc.) instead of only a class type.Here you can see that both of the input types, as well as the return type, are
const T&
, which means "constant reference to typeT
". This means the input parameters and return value are passed by reference instead of passed by value. This is like passing by pointers, and is more efficient for large types, such as class objects. Theconstexpr
part of the function modifies the function itself and indicates that the function must be capable of being evaluated at compile-time (at least if providedconstexpr
input parameters), but if it cannot be evaluated at compile-time, then it defaults back to a run-time evaluation, like any other normal function.The compile-time aspect of a
constexpr
C++ function makes it kind-of C-macro-like, in that if compile-time evaluation is possible for aconstexpr
function, it will be done at compile-time, same as aMIN()
orMAX()
macro substitution could possibly be fully evaluated at compile-time in C or C++ too. For additional references for this C++ template info, see below.4. [C++ only] C++
std::max()
If using C++, I'd like to add that the built-in
std::max()
function in the<algorithm>
header file has a variety of forms. See the "Possible implementation" section on the documentation page at the cppreference.com community wiki (https://en.cppreference.com/w/cpp/algorithm/max) for 4 possible implementations for the 4 forms ofstd::max()
.Normal usages include:
...but if you'd like to compare many numbers at once, you can use the 4th form, which accepts a
std::initializer_list<T>
, like this:Function declaration:
Usage:
References:
Clang note from Wikipedia:
Related:
避免非标准编译器扩展,并将其实现为纯标准 C (ISO 9899:2011) 中完全类型安全的宏。
解决方案
用法
说明
宏MAX根据
type
参数创建另一个宏。如果针对给定类型实现了该控制宏,则该控制宏用于检查两个参数的类型是否正确。如果不支持type
,则会出现编译器错误。如果 x 或 y 的类型不正确,
ENSURE_
宏中将会出现编译器错误。如果支持更多类型,可以添加更多此类宏。我假设仅使用算术类型(整数、浮点数、指针等),而不使用结构或数组等。如果所有类型都正确,则将调用 GENERIC_MAX 宏。每个宏参数都需要额外的括号,这是编写 C 宏时通常的标准预防措施。
然后是 C 中隐式类型提升的常见问题。
?:
运算符平衡第二个和第三个操作数。例如,GENERIC_MAX(my_char1, my_char2)
的结果将是int
。为了防止宏执行此类潜在危险的类型提升,使用了最终类型转换为预期类型。基本原理
我们希望宏的两个参数具有相同的类型。如果其中之一属于不同类型,则宏不再是类型安全的,因为像
?:
这样的运算符将产生隐式类型提升。因为确实如此,我们也总是需要将最终结果转换回预期类型,如上所述。只有一个参数的宏可以用一种更简单的方式编写。但如果有 2 个或更多参数,则需要包含额外的类型参数。因为不幸的是,这样的事情是不可能的:
问题是,如果上面的宏被调用为带有两个
int
的MAX(1, 2)
,它仍然会尝试宏 -展开_Generic
关联列表的所有可能场景。因此,ENSURE_float
宏也会被扩展,即使它与int
无关。由于该宏有意仅包含float
类型,因此代码将无法编译。为了解决这个问题,我在预处理器阶段使用 ## 运算符创建了宏名称,这样就不会意外扩展宏。
示例
Avoid non-standard compiler extensions and implement it as a completely type-safe macro in pure standard C (ISO 9899:2011).
Solution
Usage
Explanation
The macro MAX creates another macro based on the
type
parameter. This control macro, if implemented for the given type, is used to check that both parameters are of the correct type. If thetype
is not supported, there will be a compiler error.If either x or y is not of the correct type, there will be a compiler error in the
ENSURE_
macros. More such macros can be added if more types are supported. I've assumed that only arithmetic types (integers, floats, pointers etc) will be used and not structs or arrays etc.If all types are correct, the GENERIC_MAX macro will be called. Extra parenthesis are needed around each macro parameter, as the usual standard precaution when writing C macros.
Then there's the usual problems with implicit type promotions in C. The
?:
operator balances the 2nd and 3rd operand against each other. For example, the result ofGENERIC_MAX(my_char1, my_char2)
would be anint
. To prevent the macro from doing such potentially dangerous type promotions, a final type cast to the intended type was used.Rationale
We want both parameters to the macro to be of the same type. If one of them is of a different type, the macro is no longer type safe, because an operator like
?:
will yield implicit type promotions. And because it does, we also always need to cast the final result back to the intended type as explained above.A macro with just one parameter could have been written in a much simpler way. But with 2 or more parameters, there is a need to include an extra type parameter. Because something like this is unfortunately impossible:
The problem is that if the above macro is called as
MAX(1, 2)
with twoint
, it will still try to macro-expand all possible scenarios of the_Generic
association list. So theENSURE_float
macro will get expanded too, even though it isn't relevant forint
. And since that macro intentionally only contains thefloat
type, the code won't compile.To solve this, I created the macro name during the pre-processor phase instead, with the ## operator, so that no macro gets accidentally expanded.
Examples
由于最近的发展,这是一个较晚的答案。由于OP接受了依赖于不可移植的GCC(和clang)扩展
typeof
- 或__typeof__
“干净”ISO C的答案 - 有一个更好的解决方案可用gcc-4.9。此扩展的明显好处是每个宏参数仅扩展一次,与
__typeof__
解决方案不同。__auto_type
是 C++11auto
的有限形式。它不能(或不应该?)在 C++ 代码中使用,尽管在使用 C++11 时没有充分的理由不使用auto
的高级类型推断功能。也就是说,我假设当宏包含在
extern "C" { ... }
作用域中时,使用此语法不会出现任何问题;例如,来自 C 标头。 AFAIK,这个扩展还没有找到它的方式 info clangThis is a late answer, due to a fairly recent development. Since the OP accepted the answer that relies on a non-portable GCC (and clang) extension
typeof
- or__typeof__
for 'clean' ISO C - there's a better solution available as of gcc-4.9.The obvious benefit of this extension is that each macro argument is only expanded once, unlike the
__typeof__
solution.__auto_type
is a limited form of C++11'sauto
. It cannot (or should not?) be used in C++ code, though there's no good reason not to use the superior type inference capabilities ofauto
when using C++11.That said, I assume there are no issues using this syntax when the macro is included in an
extern "C" { ... }
scope; e.g., from a C header. AFAIK, this extension has not found its way info clang我不认为它们是标准化的宏。浮点已经有了标准化函数,
fmax
和fmin
(以及用于浮点的fmaxf
和用于长整型的fmaxl
)双打)。只要您意识到副作用/双重评估问题,您就可以将它们实现为宏。
在大多数情况下,您可以将其留给编译器来确定您要执行的操作并尽可能对其进行优化。虽然这在像
MAX(i++, j++)
这样使用时会导致问题,但我怀疑是否有必要一次性检查增量值的最大值。先递增,后检查。I don't think that they are standardised macros. There are standardised functions for floating point already,
fmax
andfmin
(andfmaxf
for floats, andfmaxl
for long doubles).You can implement them as macros as long as you are aware of the issues of side-effects/double-evaluation.
In most cases, you can leave it to the compiler to determine what you're trying to do and optimise it as best it can. While this causes problems when used like
MAX(i++, j++)
, I doubt there is ever much need in checking the maximum of incremented values in one go. Increment first, then check.我写了这个版本 MSVC、GCC、C 和 C++。
I wrote this version that works for MSVC, GCC, C, and C++.
如果您需要最小/最大以避免昂贵的分支,则不应使用三元运算符,因为它会编译为跳转。 Bit Twiddling Hacks 描述了一种无需使用即可实现最小/最大函数的有用方法分枝。
If you need min/max in order to avoid an expensive branch, you shouldn't use the ternary operator, as it will compile down to a jump. Bit Twiddling Hacks describes a useful method for implementing a min/max function without branching.
值得指出的是,我认为如果您使用三元运算定义
min
和max
,例如then ,对于
fmin(- 0.0,0.0)
和fmax(-0.0,0.0)
您需要交换参数It's worth pointing out I think that if you define
min
andmax
with the ternary operation such asthen to get the same result for the special case of
fmin(-0.0,0.0)
andfmax(-0.0,0.0)
you need to swap the arguments看起来像
Windef.h
(类似于#include
)有max
和min
(小写)宏,也遭受“双重评估”困难,但它们是为那些不想重新推出自己的宏而存在的:)Looks like
Windef.h
(a la#include <windows.h>
) hasmax
andmin
(lower case) macros, that also suffer from the "double evaluation" difficulty, but they're there for those that don't want to re-roll their own :)旧的 GCC 扩展:运算符
?、?=
在非常旧的 GCC 版本中,有运算符
?
(请参阅此处,这里是 C++ 语言,但我认为当时它也作为 C 扩展应用)我还看到了与赋值语句相对应的运算符
?=
。操作数被评估一次,甚至允许非常短的赋值语句。与常见的最小/最大分配相比,它非常短。没有什么可以超越这一点。
这些是以下内容的简写:
查找最小值非常简洁:
我希望有一天这可能会带回 GCC,因为我认为这些运算符非常天才。
Old GCC Extension: Operators
<?, >?, <?=, >?=
In a very old version of GCC there were the operators
<?, >?
(see here, here it was in C++ but I think it also applied as a C extension back then)I have also seen the operators
<?=, >?=
corresponding to the assignment statements.The operands were evaluated once and even allowed for a very short assignment statement. Its very short compared to common min/max assignments. There is nothing that can top this.
Those were a shorthand for the following:
Finding the minimum is very concise:
I hope this might be some day brought back to GCC, because I think these operators are genious.
正如其他答案所建议的,一个简单的解决方案是一个宏:
但是,这可能会导致意外结果和不必要的(如果
a
和b
是复杂表达式(有副作用)而不仅仅是变量,则重新计算。该问题确实要求特定于编译器的扩展,因此其他答案也表明,更安全的选择是使用 GCC 语句表达式和 __typeof__ 。
如果您有 GCC,此解决方案非常有用。但否则根本不起作用。
如果您使用 C++,模板函数会更好。但问题被标记为 c 因此,仅 C++ 的答案并不适用于此处的所有情况。
我想提供一个便携式解决方案。由于 C 库已经具有以
l
和ll
结尾的较大整数类型的函数,因此我们可以定义自己的min
和max< /code> 以同样的方式匹配库中找到的函数,例如
abs
/absl
/absll
和div< /code>/
divl
/divll
。该解决方案是 GCC 扩展或仅 C++ 功能的良好替代品,并且比宏更安全。我们没有定义
fmin
和 < a href="https://en.cppreference.com/w/c/numeric/math/fmax" rel="nofollow noreferrer">fmax
及其浮点类型的变体因为那些已经存在了。A simple solution as the other answers suggest would be a macro along the lines of:
However this can cause unexpected results and unnecessary (re)computation if
a
andb
are complex expressions (with side effects) rather than just variables.The question did ask for compiler-specific extensions so as the other answers also suggest, a safer option would be to use GCC statement expressions and
__typeof__
.This solution is great if you have GCC. But will not work at all otherwise.
A template function would be even better if you are using C++. But the question is tagged c so a C++ only answer does not apply to all cases here.
I would like to offer a portable solution. Since the C library already has functions ending in
l
andll
for larger integer types, we could define our ownmin
andmax
the same way, to match the functions found in the library, such asabs
/absl
/absll
anddiv
/divl
/divll
.This solution is a good substitute for GCC extensions or C++ only features, and is safer than macros. We do not define
fmin
andfmax
and their variants for floating point types because those already exist.我知道那个人说“C”...
但如果有机会,请使用 C++ 模板:
类型安全,并且与其他评论中提到的 ++ 没有问题。
I know the guy said "C"...
But if you have the chance, use a C++ template:
Type safe, and no problems with the ++ mentioned in other comments.
在gcc中,你可以使用这个:
在msvc中,你可以使用这个:
这样,可以确保x,y不会被计算2次
in gcc, you can use this:
in msvc, you can use this:
in this way, can make sure x, y won't be calculated 2 times
两个整数
a
和b
的最大值为(int)(0.5((a+b)+abs(ab)))
。对于双精度数,这也可以与(double)
和fabs(ab)
一起使用(与浮点数类似)The maximum of two integers
a
andb
is(int)(0.5((a+b)+abs(a-b)))
. This may also work with(double)
andfabs(a-b)
for doubles (similar for floats)最简单的方法是将其定义为
.h
文件中的全局函数,如果您的程序是包含大量文件的模块化程序,则可以随时调用它。如果没有, double MIN(a,b){return (a 是最简单的方法。The simplest way is to define it as a global function in a
.h
file, and call it whenever you want, if your program is modular with lots of files. If not,double MIN(a,b){return (a<b?a:b)}
is the simplest way.