C中的MIN和MAX

时间:2010-08-09 04:49:40

标签: c max min c-preprocessor

C中定义MINMAX的位置,如果有的话?

实现这些的最佳方式是什么,尽可能安全且类型安全? (首选编译器扩展/内置主流编译器。)

16 个答案:

答案 0 :(得分:332)

  

C中定义MINMAX的位置,如果有的话?

他们不是。

  

实现这些的最佳方法是什么,尽可能通用和类型安全(首选编译器扩展/内置主流编译器)。

作为功能。我不会使用像#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))这样的宏,特别是如果您计划部署代码。要么自己编写,要么使用标准fmaxfmin之类的内容,要么使用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::minstd::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)

我不认为它们是标准化的宏。已有标准化的浮点函数,fmaxfminfmaxf表示浮点数,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>)有maxmin(小写)宏,它们也遭受“双重评估”难度,但是那些不想重新推销自己的人:)

答案 9 :(得分:3)

我知道那个人说“C”...... 但如果有机会,请使用C ++模板:

template<class T> T min(T a, T b) { return a < b ? a : b; }

输入安全,其他评论中提到的++没有问题。

答案 10 :(得分:3)

值得指出的是,如果您使用第三级定义minmax

#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()以进行复制从这里粘贴更容易。 :)

1。旧的C宏方式:

这项技术是常用的,受到那些知道如何正确使用它,“事实上”的做事方式的人的推崇,如果使用得当,可以使用,但 buggy (认为:double-evaluation side effect),如果您曾经通过包含变量赋值的表达式进行比较:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

2。新的和改进的gcc“ statement expression”方式:

此技术避免了上述“双重评估”的副作用和错误,因此被认为是实现此目的的更优越,更安全和“更现代”的 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 ++,则可能建议使用模板代替这种构造。

参考文献:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. MIN and MAX in C

答案 12 :(得分:1)

旧的 GCC 扩展:运算符 <?, >?, <?=, >?=

在一个非常旧的 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)

两个整数ab的最大值为(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)}是最简单的方法。