当前位置:  开发笔记 > 编程语言 > 正文

C中的MIN和MAX

如何解决《C中的MIN和MAX》经验,为你挑选了8个好方法。

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

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



1> David Titare..:

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

他们不是.

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

作为功​​能.我不会使用宏#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)),特别是如果您计划部署代码.无论是写自己的,使用类似标准fmaxfmin,或使用固定宏海湾合作委员会的typeof(你的类型安全奖励太):

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

每个人都说"哦,我知道双重评估,这没有问题",几个月后,你将在几个小时内调试最愚蠢的问题.

注意使用__typeof__而不是typeof:

如果您正在编写包含在ISO C程序中必须工作的头文件,请编写__typeof__而不是 typeof.


你知道,如果gcc有一个警告:'警告:带有副作用的表达式在使用点被多重评估',它会非常方便...
@caf:这不需要预处理器有更复杂的C语法知识吗?
@Soumen例如,如果你调用`MIN(x ++,y ++)`,预处理器将生成以下代码`(((x ++)<(y ++))?(x ++):( y ++))`.所以,`x`和`y`将增加两次.
@dreamlax我曾经看过一个人用`MAX(someUpperBound,someRandomFunction())`将一个随机值限制到某个上限的情况.这是一个糟糕的想法,但它甚至没有工作,因为他使用的'MAX`有双重评估问题,所以他得到的结果与最初评估的随机数不同.
经过多次尝试弄清楚,我认为无论如何都不会在VC++中做到这一点,但你最好的办法是尝试搞乱MSVC++ 2010新的`decltype`关键字 - 但即便如此,Visual Studio也无法复合宏中的语句(和`decltype`无论如何都是C++),即GCC的`({...})`语法,所以我很确定它是不可能的.关于这个问题,我没有看过任何其他编译器,抱歉路德:S
另请参见http://stackoverflow.com/questions/5595593/min-macro-in-kernel-h - 与此类似,但对类型安全性进行了额外检查.
`警告:ISO C禁止在表达式[-Wpedantic]中使用支撑组

2> Mikel..:

它也在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的



3> dan04..:

有一个std::minstd::maxC++,但AFAIK,在C标准库中没有等价物.您可以使用宏来自己定义它们

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

但如果你写一些像这样的东西会导致问题MAX(++a, ++b).


@Makouda:宏中的额外括号有助于避免运算符优先级问题.例如,考虑`#define MULT(x,y)x*y`.然后`MULT(a + b,a + b)`扩展为'a + b*a + b`,由于优先级,它解析为'a +(b*a)+ b`.这不是程序员可能想要的.
为什么要放太多括号??? 我发现了一个测验,他们说`#define MIN(A,B)((A
4> Lundin..:

避免使用非标准编译器扩展,并将其作为纯标准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;
}



5> Brett Hale..:

由于最近的发展,这是一个迟到的答案.由于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



6> dreamlax..:

我不认为它们是标准化的宏.有浮点已经标准化的功能,fmax并且fmin(和fmaxf对花车,和fmaxl长期双打).

只要您了解副作用/双重评估的问题,就可以将它们实现为宏.

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

在大多数情况下,您可以将其留给编译器来确定您要执行的操作并尽可能地优化它.虽然这在使用时会引起问题MAX(i++, j++),但我怀疑一次性检查增量值的最大值是非常需要的.先增加,然后检查.



7> Matt Joiner..:

我写了这个版本,对于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



8> cib..:

如果你需要min/max以避免昂贵的分支,你不应该使用三元运算符,因为它会编译成跳转.下面的链接描述了一种在没有分支的情况下实现最小/最大功能的有用方法.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax


如果启用了优化,在大多数情况下,所有现代编译器都会发出条件移动而不是分支,因此使用这样的黑客几乎没有意义.
推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有